summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-16 06:48:32 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-16 06:48:32 +0000
commit109763bb929004fefa409c342d1c14a78bfe126f (patch)
tree0614c618674684955caa470ee49d26e05e7dc3e1 /chrome/browser/sync
parent457764525fd9a27612d7c8bc9d77ae0646db1a56 (diff)
downloadchromium_src-109763bb929004fefa409c342d1c14a78bfe126f.zip
chromium_src-109763bb929004fefa409c342d1c14a78bfe126f.tar.gz
chromium_src-109763bb929004fefa409c342d1c14a78bfe126f.tar.bz2
Made sync code build and pass unit tests on OS X.
Major changes: - Moved sync_setup_{flow,wizard} to sync directory. - Made browser_with_test_window_test compile on non-Windows platform. - Moved localized contents font util functions to app/. BUG=23073 TEST=trybot Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=29253 Review URL: http://codereview.chromium.org/279004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29255 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sync')
-rw-r--r--chrome/browser/sync/engine/syncer.cc2
-rw-r--r--chrome/browser/sync/engine/syncer_thread.cc2
-rw-r--r--chrome/browser/sync/profile_sync_service.h2
-rw-r--r--chrome/browser/sync/sync_setup_flow.cc304
-rw-r--r--chrome/browser/sync/sync_setup_flow.h189
-rw-r--r--chrome/browser/sync/sync_setup_wizard.cc199
-rw-r--r--chrome/browser/sync/sync_setup_wizard.h82
-rw-r--r--chrome/browser/sync/sync_setup_wizard_unittest.cc376
-rw-r--r--chrome/browser/sync/util/character_set_converters.h15
-rw-r--r--chrome/browser/sync/util/user_settings_unittest.cc12
10 files changed, 1173 insertions, 10 deletions
diff --git a/chrome/browser/sync/engine/syncer.cc b/chrome/browser/sync/engine/syncer.cc
index 9274edf..de8b1b2 100644
--- a/chrome/browser/sync/engine/syncer.cc
+++ b/chrome/browser/sync/engine/syncer.cc
@@ -118,7 +118,7 @@ void Syncer::SyncShare(SyncerSession* session,
// Reset silenced_until_, it is the callers responsibility to honor throttles.
silenced_until_ = session->silenced_until();
- SyncerStep next_step;
+ SyncerStep next_step = current_step;
while (!ExitRequested()) {
switch (current_step) {
case SYNCER_BEGIN:
diff --git a/chrome/browser/sync/engine/syncer_thread.cc b/chrome/browser/sync/engine/syncer_thread.cc
index 073b5ea..0861940 100644
--- a/chrome/browser/sync/engine/syncer_thread.cc
+++ b/chrome/browser/sync/engine/syncer_thread.cc
@@ -75,7 +75,7 @@ int UserIdleTime() {
}
int64 idle_time; // in nanoseconds
- Boolean success;
+ Boolean success = false;
if (CFGetTypeID(object) == CFNumberGetTypeID()) {
success = CFNumberGetValue((CFNumberRef)object,
kCFNumberSInt64Type,
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index fab7ea5..9eedbef 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -20,7 +20,7 @@
#include "chrome/browser/sync/glue/change_processor.h"
#include "chrome/browser/sync/glue/model_associator.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
-#include "chrome/browser/views/sync/sync_setup_wizard.h"
+#include "chrome/browser/sync/sync_setup_wizard.h"
#include "chrome/common/notification_registrar.h"
#include "googleurl/src/gurl.h"
diff --git a/chrome/browser/sync/sync_setup_flow.cc b/chrome/browser/sync/sync_setup_flow.cc
new file mode 100644
index 0000000..2784572
--- /dev/null
+++ b/chrome/browser/sync/sync_setup_flow.cc
@@ -0,0 +1,304 @@
+// Copyright (c) 2009 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.
+
+#ifdef CHROME_PERSONALIZATION
+
+#include "chrome/browser/sync/sync_setup_flow.h"
+
+#include "app/gfx/font.h"
+#include "app/gfx/font_util.h"
+#include "base/histogram.h"
+#include "base/json_reader.h"
+#include "base/json_writer.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/renderer_host/render_view_host.h"
+#include "chrome/browser/sync/auth_error_state.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/pref_names.h"
+#include "grit/locale_settings.h"
+
+// XPath expression for finding specific iframes.
+static const wchar_t* kLoginIFrameXPath = L"//iframe[@id='login']";
+static const wchar_t* kMergeIFrameXPath = L"//iframe[@id='merge']";
+static const wchar_t* kDoneIframeXPath = L"//iframe[@id='done']";
+
+// Helper function to read the JSON string from the Value parameter.
+static std::string GetJsonResponse(const Value* content) {
+ if (!content || !content->IsType(Value::TYPE_LIST)) {
+ NOTREACHED();
+ return std::string();
+ }
+ const ListValue* args = static_cast<const ListValue*>(content);
+ if (args->GetSize() != 1) {
+ NOTREACHED();
+ return std::string();
+ }
+
+ std::string result;
+ Value* value = NULL;
+ if (!args->Get(0, &value) || !value->GetAsString(&result)) {
+ NOTREACHED();
+ return std::string();
+ }
+
+ return result;
+}
+
+void FlowHandler::RegisterMessages() {
+ dom_ui_->RegisterMessageCallback("SubmitAuth",
+ NewCallback(this, &FlowHandler::HandleSubmitAuth));
+ dom_ui_->RegisterMessageCallback("SubmitMergeAndSync",
+ NewCallback(this, &FlowHandler::HandleSubmitMergeAndSync));
+}
+
+static bool GetUsernameAndPassword(const std::string& json,
+ std::string* username, std::string* password) {
+ scoped_ptr<Value> parsed_value(JSONReader::Read(json, false));
+ if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
+ return false;
+
+ DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get());
+ if (!result->GetString(L"user", username) ||
+ !result->GetString(L"pass", password)) {
+ return false;
+ }
+ return true;
+}
+
+void FlowHandler::HandleSubmitAuth(const Value* value) {
+ std::string json(GetJsonResponse(value));
+ std::string username, password;
+ if (json.empty())
+ return;
+
+ if (!GetUsernameAndPassword(json, &username, &password)) {
+ // The page sent us something that we didn't understand.
+ // This probably indicates a programming error.
+ NOTREACHED();
+ return;
+ }
+
+ if (flow_)
+ flow_->OnUserSubmittedAuth(username, password);
+}
+
+void FlowHandler::HandleSubmitMergeAndSync(const Value* value) {
+ if (flow_)
+ flow_->OnUserAcceptedMergeAndSync();
+}
+
+// Called by SyncSetupFlow::Advance.
+void FlowHandler::ShowGaiaLogin(const DictionaryValue& args) {
+ std::string json;
+ JSONWriter::Write(&args, false, &json);
+ std::wstring javascript = std::wstring(L"showGaiaLogin") +
+ L"(" + UTF8ToWide(json) + L");";
+ ExecuteJavascriptInIFrame(kLoginIFrameXPath, javascript);
+}
+
+void FlowHandler::ShowGaiaSuccessAndClose() {
+ ExecuteJavascriptInIFrame(kLoginIFrameXPath, L"showGaiaSuccessAndClose();");
+}
+
+void FlowHandler::ShowGaiaSuccessAndSettingUp() {
+ ExecuteJavascriptInIFrame(kLoginIFrameXPath,
+ L"showGaiaSuccessAndSettingUp();");
+}
+
+void FlowHandler::ShowMergeAndSync() {
+ if (dom_ui_) // NULL during testing.
+ dom_ui_->CallJavascriptFunction(L"showMergeAndSync");
+}
+
+void FlowHandler::ShowSetupDone(const std::wstring& user) {
+ StringValue synced_to_string(WideToUTF8(l10n_util::GetStringF(
+ IDS_SYNC_NTP_SYNCED_TO, user)));
+ std::string json;
+ JSONWriter::Write(&synced_to_string, false, &json);
+ std::wstring javascript = std::wstring(L"setSyncedToUser") +
+ L"(" + UTF8ToWide(json) + L");";
+ ExecuteJavascriptInIFrame(kDoneIframeXPath, javascript);
+
+ if (dom_ui_)
+ dom_ui_->CallJavascriptFunction(L"showSetupDone", synced_to_string);
+}
+
+void FlowHandler::ShowFirstTimeDone(const std::wstring& user) {
+ ExecuteJavascriptInIFrame(kDoneIframeXPath,
+ L"setShowFirstTimeSetupSummary();");
+ ShowSetupDone(user);
+}
+
+void FlowHandler::ShowMergeAndSyncError() {
+ ExecuteJavascriptInIFrame(kMergeIFrameXPath, L"showMergeAndSyncError();");
+}
+
+void FlowHandler::ExecuteJavascriptInIFrame(const std::wstring& iframe_xpath,
+ const std::wstring& js) {
+ if (dom_ui_) {
+ RenderViewHost* rvh = dom_ui_->tab_contents()->render_view_host();
+ rvh->ExecuteJavascriptInWebFrame(iframe_xpath, js);
+ }
+}
+
+SyncSetupFlow::~SyncSetupFlow() {
+ flow_handler_->set_flow(NULL);
+}
+
+void SyncSetupFlow::GetDialogSize(gfx::Size* size) const {
+ PrefService* prefs = service_->profile()->GetPrefs();
+ gfx::Font approximate_web_font = gfx::Font::CreateFont(
+ prefs->GetString(prefs::kWebKitSansSerifFontFamily),
+ prefs->GetInteger(prefs::kWebKitDefaultFontSize));
+
+ gfx::Size s = gfx::GetLocalizedContentsSizeForFont(
+ IDS_SYNC_SETUP_WIZARD_WIDTH_CHARS,
+ IDS_SYNC_SETUP_WIZARD_HEIGHT_LINES,
+ approximate_web_font);
+
+ size->set_width(s.width());
+ size->set_height(s.height());
+}
+
+// A callback to notify the delegate that the dialog closed.
+void SyncSetupFlow::OnDialogClosed(const std::string& json_retval) {
+ DCHECK(json_retval.empty());
+ container_->set_flow(NULL); // Sever ties from the wizard.
+ if (current_state_ == SyncSetupWizard::DONE ||
+ current_state_ == SyncSetupWizard::DONE_FIRST_TIME) {
+ service_->SetSyncSetupCompleted();
+ }
+
+ // Record the state at which the user cancelled the signon dialog.
+ switch (current_state_) {
+ case SyncSetupWizard::GAIA_LOGIN:
+ ProfileSyncService::SyncEvent(
+ ProfileSyncService::CANCEL_FROM_SIGNON_WITHOUT_AUTH);
+ break;
+ case SyncSetupWizard::GAIA_SUCCESS:
+ ProfileSyncService::SyncEvent(
+ ProfileSyncService::CANCEL_DURING_SIGNON);
+ break;
+ case SyncSetupWizard::MERGE_AND_SYNC:
+ ProfileSyncService::SyncEvent(
+ ProfileSyncService::CANCEL_DURING_SIGNON_AFTER_MERGE);
+ break;
+ case SyncSetupWizard::DONE_FIRST_TIME:
+ case SyncSetupWizard::DONE:
+ UMA_HISTOGRAM_MEDIUM_TIMES("Sync.UserPerceivedAuthorizationTime",
+ base::TimeTicks::Now() - login_start_time_);
+ break;
+ default:
+ break;
+ }
+
+ service_->OnUserCancelledDialog();
+ delete this;
+}
+
+// static
+void SyncSetupFlow::GetArgsForGaiaLogin(const ProfileSyncService* service,
+ DictionaryValue* args) {
+ AuthErrorState error(service->GetAuthErrorState());
+ if (!service->last_attempted_user_email().empty()) {
+ args->SetString(L"user", service->last_attempted_user_email());
+ args->SetInteger(L"error", error);
+ } else {
+ std::wstring user(UTF16ToWide(service->GetAuthenticatedUsername()));
+ args->SetString(L"user", user);
+ args->SetInteger(L"error", user.empty() ? 0 : error);
+ }
+}
+
+void SyncSetupFlow::GetDOMMessageHandlers(
+ std::vector<DOMMessageHandler*>* handlers) const {
+ handlers->push_back(flow_handler_);
+}
+
+bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) {
+ switch (state) {
+ case SyncSetupWizard::GAIA_LOGIN:
+ return current_state_ == SyncSetupWizard::GAIA_LOGIN;
+ case SyncSetupWizard::GAIA_SUCCESS:
+ return current_state_ == SyncSetupWizard::GAIA_LOGIN;
+ case SyncSetupWizard::MERGE_AND_SYNC:
+ return current_state_ == SyncSetupWizard::GAIA_SUCCESS;
+ case SyncSetupWizard::FATAL_ERROR:
+ return true; // You can always hit the panic button.
+ case SyncSetupWizard::DONE_FIRST_TIME:
+ case SyncSetupWizard::DONE:
+ return current_state_ == SyncSetupWizard::MERGE_AND_SYNC ||
+ current_state_ == SyncSetupWizard::GAIA_SUCCESS;
+ default:
+ NOTREACHED() << "Unhandled State: " << state;
+ return false;
+ }
+}
+
+void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) {
+ if (!ShouldAdvance(advance_state))
+ return;
+ switch (advance_state) {
+ case SyncSetupWizard::GAIA_LOGIN: {
+ DictionaryValue args;
+ SyncSetupFlow::GetArgsForGaiaLogin(service_, &args);
+ flow_handler_->ShowGaiaLogin(args);
+ break;
+ }
+ case SyncSetupWizard::GAIA_SUCCESS:
+ if (end_state_ == SyncSetupWizard::GAIA_SUCCESS)
+ flow_handler_->ShowGaiaSuccessAndClose();
+ else
+ flow_handler_->ShowGaiaSuccessAndSettingUp();
+ break;
+ case SyncSetupWizard::MERGE_AND_SYNC:
+ flow_handler_->ShowMergeAndSync();
+ break;
+ case SyncSetupWizard::FATAL_ERROR:
+ if (current_state_ == SyncSetupWizard::MERGE_AND_SYNC)
+ flow_handler_->ShowMergeAndSyncError();
+ break;
+ case SyncSetupWizard::DONE_FIRST_TIME:
+ flow_handler_->ShowFirstTimeDone(
+ UTF16ToWide(service_->GetAuthenticatedUsername()));
+ break;
+ case SyncSetupWizard::DONE:
+ flow_handler_->ShowSetupDone(
+ UTF16ToWide(service_->GetAuthenticatedUsername()));
+ break;
+ default:
+ NOTREACHED() << "Invalid advance state: " << advance_state;
+ }
+ current_state_ = advance_state;
+}
+
+// static
+SyncSetupFlow* SyncSetupFlow::Run(ProfileSyncService* service,
+ SyncSetupFlowContainer* container,
+ SyncSetupWizard::State start,
+ SyncSetupWizard::State end) {
+ DictionaryValue args;
+ if (start == SyncSetupWizard::GAIA_LOGIN)
+ SyncSetupFlow::GetArgsForGaiaLogin(service, &args);
+ std::string json_args;
+ JSONWriter::Write(&args, false, &json_args);
+
+ Browser* b = BrowserList::GetLastActive();
+ if (!b)
+ return NULL;
+
+ FlowHandler* handler = new FlowHandler();
+ SyncSetupFlow* flow = new SyncSetupFlow(start, end, json_args,
+ container, handler, service);
+ handler->set_flow(flow);
+ b->BrowserShowHtmlDialog(flow, NULL);
+ return flow;
+}
+
+#endif // CHROME_PERSONALIZATION
diff --git a/chrome/browser/sync/sync_setup_flow.h b/chrome/browser/sync/sync_setup_flow.h
new file mode 100644
index 0000000..32e3db1
--- /dev/null
+++ b/chrome/browser/sync/sync_setup_flow.h
@@ -0,0 +1,189 @@
+// Copyright (c) 2006-2008 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.
+
+#ifdef CHROME_PERSONALIZATION
+
+#ifndef CHROME_BROWSER_SYNC_SYNC_SETUP_FLOW_H_
+#define CHROME_BROWSER_SYNC_SYNC_SETUP_FLOW_H_
+
+#include <string>
+#include <vector>
+
+#include "app/l10n_util.h"
+#include "base/time.h"
+#include "chrome/browser/dom_ui/html_dialog_ui.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/sync_setup_wizard.h"
+#include "grit/generated_resources.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+class FlowHandler;
+class SyncSetupFlowContainer;
+
+// The state machine used by SyncSetupWizard, exposed in its own header
+// to facilitate testing of SyncSetupWizard. This class is used to open and
+// run the html dialog and deletes itself when the dialog closes.
+class SyncSetupFlow : public HtmlDialogUIDelegate {
+ public:
+ virtual ~SyncSetupFlow();
+
+ // Runs a flow from |start| to |end|, and does the work of actually showing
+ // the HTML dialog. |container| is kept up-to-date with the lifetime of the
+ // flow (e.g it is emptied on dialog close).
+ static SyncSetupFlow* Run(ProfileSyncService* service,
+ SyncSetupFlowContainer* container,
+ SyncSetupWizard::State start,
+ SyncSetupWizard::State end);
+
+ // Fills |args| with "user" and "error" arguments by querying |service|.
+ static void GetArgsForGaiaLogin(
+ const ProfileSyncService* service,
+ DictionaryValue* args);
+
+ // Triggers a state machine transition to advance_state.
+ void Advance(SyncSetupWizard::State advance_state);
+
+ // HtmlDialogUIDelegate implementation.
+ // Get the HTML file path for the content to load in the dialog.
+ virtual GURL GetDialogContentURL() const {
+ return GURL("chrome://syncresources/setup");
+ }
+
+ // HtmlDialogUIDelegate implementation.
+ virtual void GetDOMMessageHandlers(
+ std::vector<DOMMessageHandler*>* handlers) const;
+
+ // HtmlDialogUIDelegate implementation.
+ // Get the size of the dialog.
+ virtual void GetDialogSize(gfx::Size* size) const;
+
+ // HtmlDialogUIDelegate implementation.
+ // Gets the JSON string input to use when opening the dialog.
+ virtual std::string GetDialogArgs() const {
+ return dialog_start_args_;
+ }
+
+ // HtmlDialogUIDelegate implementation.
+ // A callback to notify the delegate that the dialog closed.
+ virtual void OnDialogClosed(const std::string& json_retval);
+
+ // HtmlDialogUIDelegate implementation.
+ virtual std::wstring GetDialogTitle() const {
+ return l10n_util::GetString(IDS_SYNC_MY_BOOKMARKS_LABEL);
+ }
+
+ // HtmlDialogUIDelegate implementation.
+ virtual bool IsDialogModal() const {
+ return false;
+ }
+
+ void OnUserSubmittedAuth(const std::string& username,
+ const std::string& password) {
+ service_->OnUserSubmittedAuth(username, password);
+ }
+
+ void OnUserAcceptedMergeAndSync() {
+ service_->OnUserAcceptedMergeAndSync();
+ }
+
+ private:
+ FRIEND_TEST(SyncSetupWizardTest, InitialStepLogin);
+ FRIEND_TEST(SyncSetupWizardTest, InitialStepMergeAndSync);
+ FRIEND_TEST(SyncSetupWizardTest, DialogCancelled);
+ FRIEND_TEST(SyncSetupWizardTest, InvalidTransitions);
+ FRIEND_TEST(SyncSetupWizardTest, FullSuccessfulRunSetsPref);
+ FRIEND_TEST(SyncSetupWizardTest, DiscreteRun);
+
+ // Use static Run method to get an instance.
+ SyncSetupFlow(SyncSetupWizard::State start_state,
+ SyncSetupWizard::State end_state,
+ const std::string& args, SyncSetupFlowContainer* container,
+ FlowHandler* handler, ProfileSyncService* service)
+ : container_(container),
+ dialog_start_args_(args),
+ current_state_(start_state),
+ end_state_(end_state),
+ login_start_time_(base::TimeTicks::Now()),
+ flow_handler_(handler),
+ service_(service) {
+ }
+
+ // Returns true if |this| should transition its state machine to |state|
+ // based on |current_state_|, or false if that would be nonsense or is
+ // a no-op.
+ bool ShouldAdvance(SyncSetupWizard::State state);
+
+ SyncSetupFlowContainer* container_; // Our container. Don't own this.
+ std::string dialog_start_args_; // The args to pass to the initial page.
+
+ SyncSetupWizard::State current_state_;
+ SyncSetupWizard::State end_state_; // The goal.
+
+ // Time that the GAIA_LOGIN step was received.
+ base::TimeTicks login_start_time_;
+
+ // The handler needed for the entire flow. We don't own this.
+ FlowHandler* flow_handler_;
+
+ // We need this to write the sentinel "setup completed" pref.
+ ProfileSyncService* service_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncSetupFlow);
+};
+
+// A really simple wrapper for a SyncSetupFlow so that we don't have to
+// add any public methods to the public SyncSetupWizard interface to notify it
+// when the dialog closes.
+class SyncSetupFlowContainer {
+ public:
+ SyncSetupFlowContainer() : flow_(NULL) { }
+ void set_flow(SyncSetupFlow* flow) {
+ DCHECK(!flow_ || !flow);
+ flow_ = flow;
+ }
+
+ SyncSetupFlow* get_flow() { return flow_; }
+ private:
+ SyncSetupFlow* flow_;
+
+ DISALLOW_COPY_AND_ASSIGN(SyncSetupFlowContainer);
+};
+
+// The FlowHandler connects the state machine to the dialog backing HTML and
+// JS namespace by implementing DOMMessageHandler and being invoked by the
+// SyncSetupFlow. Exposed here to facilitate testing.
+class FlowHandler : public DOMMessageHandler {
+ public:
+ FlowHandler() {}
+ virtual ~FlowHandler() {}
+
+ // DOMMessageHandler implementation.
+ virtual void RegisterMessages();
+
+ // Callbacks from the page.
+ void HandleSubmitAuth(const Value* value);
+ void HandleSubmitMergeAndSync(const Value* value);
+
+ // These functions control which part of the HTML is visible.
+ void ShowGaiaLogin(const DictionaryValue& args);
+ void ShowGaiaSuccessAndClose();
+ void ShowGaiaSuccessAndSettingUp();
+ void ShowMergeAndSync();
+ void ShowMergeAndSyncError();
+ void ShowSetupDone(const std::wstring& user);
+ void ShowFirstTimeDone(const std::wstring& user);
+
+ void set_flow(SyncSetupFlow* flow) {
+ flow_ = flow;
+ }
+
+ private:
+ void ExecuteJavascriptInIFrame(const std::wstring& iframe_xpath,
+ const std::wstring& js);
+ SyncSetupFlow* flow_;
+ DISALLOW_COPY_AND_ASSIGN(FlowHandler);
+};
+
+#endif // CHROME_BROWSER_SYNC_SYNC_SETUP_FLOW_H_
+#endif // CHROME_PERSONALIZATION
diff --git a/chrome/browser/sync/sync_setup_wizard.cc b/chrome/browser/sync/sync_setup_wizard.cc
new file mode 100644
index 0000000..7d00ab9
--- /dev/null
+++ b/chrome/browser/sync/sync_setup_wizard.cc
@@ -0,0 +1,199 @@
+// Copyright (c) 2006-2008 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.
+
+#ifdef CHROME_PERSONALIZATION
+
+#include "chrome/browser/sync/sync_setup_wizard.h"
+
+#include "app/resource_bundle.h"
+#include "base/message_loop.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/sync_setup_flow.h"
+#include "chrome/common/jstemplate_builder.h"
+#include "chrome/common/url_constants.h"
+#include "grit/app_resources.h"
+#include "grit/browser_resources.h"
+
+class SyncResourcesSource : public ChromeURLDataManager::DataSource {
+ public:
+ SyncResourcesSource()
+ : DataSource(chrome::kSyncResourcesHost, MessageLoop::current()) {
+ }
+ virtual ~SyncResourcesSource() { }
+
+ virtual void StartDataRequest(const std::string& path, int request_id);
+
+ virtual std::string GetMimeType(const std::string& path) const {
+ if (path == chrome::kSyncThrobberPath)
+ return "image/png";
+ else
+ return "text/html";
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SyncResourcesSource);
+};
+
+void SyncResourcesSource::StartDataRequest(const std::string& path_raw,
+ int request_id) {
+ scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
+ if (path_raw == chrome::kSyncThrobberPath) {
+ ResourceBundle::GetSharedInstance().LoadImageResourceBytes(IDR_THROBBER,
+ &html_bytes->data);
+ SendResponse(request_id, html_bytes);
+ return;
+ }
+
+ std::string response;
+ if (path_raw == chrome::kSyncGaiaLoginPath) {
+ DictionaryValue localized_strings;
+ localized_strings.SetString(L"settingupsync",
+ l10n_util::GetString(IDS_SYNC_LOGIN_SETTING_UP_SYNC));
+ localized_strings.SetString(L"introduction",
+ l10n_util::GetString(IDS_SYNC_LOGIN_INTRODUCTION));
+ localized_strings.SetString(L"signinprefix",
+ l10n_util::GetString(IDS_SYNC_LOGIN_SIGNIN_PREFIX));
+ localized_strings.SetString(L"signinsuffix",
+ l10n_util::GetString(IDS_SYNC_LOGIN_SIGNIN_SUFFIX));
+ localized_strings.SetString(L"cannotbeblank",
+ l10n_util::GetString(IDS_SYNC_CANNOT_BE_BLANK));
+ localized_strings.SetString(L"emaillabel",
+ l10n_util::GetString(IDS_SYNC_LOGIN_EMAIL));
+ localized_strings.SetString(L"passwordlabel",
+ l10n_util::GetString(IDS_SYNC_LOGIN_PASSWORD));
+ localized_strings.SetString(L"invalidcredentials",
+ l10n_util::GetString(IDS_SYNC_INVALID_USER_CREDENTIALS));
+ localized_strings.SetString(L"signin",
+ l10n_util::GetString(IDS_SYNC_SIGNIN));
+ localized_strings.SetString(L"couldnotconnect",
+ l10n_util::GetString(IDS_SYNC_LOGIN_COULD_NOT_CONNECT));
+ localized_strings.SetString(L"cannotaccessaccount",
+ l10n_util::GetString(IDS_SYNC_CANNOT_ACCESS_ACCOUNT));
+ localized_strings.SetString(L"createaccount",
+ l10n_util::GetString(IDS_SYNC_CREATE_ACCOUNT));
+ localized_strings.SetString(L"cancel",
+ l10n_util::GetString(IDS_CANCEL));
+ localized_strings.SetString(L"settingup",
+ l10n_util::GetString(IDS_SYNC_LOGIN_SETTING_UP));
+ localized_strings.SetString(L"success",
+ l10n_util::GetString(IDS_SYNC_SUCCESS));
+ localized_strings.SetString(L"errorsigningin",
+ l10n_util::GetString(IDS_SYNC_ERROR_SIGNING_IN));
+ static const base::StringPiece html(ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(IDR_GAIA_LOGIN_HTML));
+ SetFontAndTextDirection(&localized_strings);
+ response = jstemplate_builder::GetI18nTemplateHtml(
+ html, &localized_strings);
+ } else if (path_raw == chrome::kSyncMergeAndSyncPath) {
+ DictionaryValue localized_strings;
+ localized_strings.SetString(L"introduction",
+ l10n_util::GetString(IDS_SYNC_MERGE_INTRODUCTION));
+ localized_strings.SetString(L"mergeandsynclabel",
+ l10n_util::GetString(IDS_SYNC_MERGE_AND_SYNC_LABEL));
+ localized_strings.SetString(L"abortlabel",
+ l10n_util::GetString(IDS_ABORT));
+ localized_strings.SetString(L"closelabel",
+ l10n_util::GetString(IDS_CLOSE));
+ localized_strings.SetString(L"mergeandsyncwarning",
+ l10n_util::GetString(IDS_SYNC_MERGE_WARNING));
+ localized_strings.SetString(L"setuperror",
+ l10n_util::GetString(IDS_SYNC_SETUP_ERROR));
+
+ static const base::StringPiece html(ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(IDR_MERGE_AND_SYNC_HTML));
+ SetFontAndTextDirection(&localized_strings);
+ response = jstemplate_builder::GetI18nTemplateHtml(
+ html, &localized_strings);
+ } else if (path_raw == chrome::kSyncSetupDonePath) {
+ DictionaryValue localized_strings;
+ localized_strings.SetString(L"success",
+ l10n_util::GetString(IDS_SYNC_SUCCESS));
+ localized_strings.SetString(L"setupsummary",
+ l10n_util::GetString(IDS_SYNC_SETUP_ALL_DONE));
+ localized_strings.SetString(L"firsttimesetupsummary",
+ l10n_util::GetString(IDS_SYNC_SETUP_FIRST_TIME_ALL_DONE));
+ localized_strings.SetString(L"okay",
+ l10n_util::GetString(IDS_SYNC_SETUP_OK_BUTTON_LABEL));
+ static const base::StringPiece html(ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(IDR_SYNC_SETUP_DONE_HTML));
+ SetFontAndTextDirection(&localized_strings);
+ response = jstemplate_builder::GetI18nTemplateHtml(
+ html, &localized_strings);
+ } else if (path_raw == chrome::kSyncSetupFlowPath) {
+ static const base::StringPiece html(ResourceBundle::GetSharedInstance()
+ .GetRawDataResource(IDR_SYNC_SETUP_FLOW_HTML));
+ response = html.as_string();
+ }
+ // Send the response.
+ html_bytes->data.resize(response.size());
+ std::copy(response.begin(), response.end(), html_bytes->data.begin());
+ SendResponse(request_id, html_bytes);
+}
+
+SyncSetupWizard::SyncSetupWizard(ProfileSyncService* service)
+ : service_(service),
+ flow_container_(new SyncSetupFlowContainer()) {
+ // Register data sources for HTML content we require.
+ // g_browser_process and/or io_thread may not exist during testing.
+ if (g_browser_process && g_browser_process->io_thread()) {
+ // Add our network layer data source for 'cloudy' URLs.
+ g_browser_process->io_thread()->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(&chrome_url_data_manager,
+ &ChromeURLDataManager::AddDataSource,
+ new SyncResourcesSource()));
+ }
+}
+
+SyncSetupWizard::~SyncSetupWizard() {
+ delete flow_container_;
+}
+
+void SyncSetupWizard::Step(State advance_state) {
+ SyncSetupFlow* flow = flow_container_->get_flow();
+ if (flow) {
+ // A setup flow is in progress and dialog is currently showing.
+ flow->Advance(advance_state);
+ } else if (!service_->profile()->GetPrefs()->GetBoolean(
+ prefs::kSyncHasSetupCompleted)) {
+ if (IsTerminalState(advance_state))
+ return;
+ // No flow is in progress, and we have never escorted the user all the
+ // way through the wizard flow.
+ flow_container_->set_flow(
+ SyncSetupFlow::Run(service_, flow_container_, advance_state, DONE));
+ } else {
+ // No flow in in progress, but we've finished the wizard flow once before.
+ // This is just a discrete run.
+ if (IsTerminalState(advance_state))
+ return; // Nothing to do.
+ flow_container_->set_flow(SyncSetupFlow::Run(service_, flow_container_,
+ advance_state, GetEndStateForDiscreteRun(advance_state)));
+ }
+}
+
+// static
+bool SyncSetupWizard::IsTerminalState(State advance_state) {
+ return advance_state == GAIA_SUCCESS ||
+ advance_state == DONE ||
+ advance_state == DONE_FIRST_TIME ||
+ advance_state == FATAL_ERROR;
+}
+
+bool SyncSetupWizard::IsVisible() const {
+ return flow_container_->get_flow() != NULL;
+}
+
+// static
+SyncSetupWizard::State SyncSetupWizard::GetEndStateForDiscreteRun(
+ State start_state) {
+ State result = start_state == GAIA_LOGIN ? GAIA_SUCCESS : DONE;
+ DCHECK_NE(DONE, result) <<
+ "Invalid start state for discrete run: " << start_state;
+ return result;
+}
+
+#endif // CHROME_PERSONALIZATION
diff --git a/chrome/browser/sync/sync_setup_wizard.h b/chrome/browser/sync/sync_setup_wizard.h
new file mode 100644
index 0000000..d8a0d95
--- /dev/null
+++ b/chrome/browser/sync/sync_setup_wizard.h
@@ -0,0 +1,82 @@
+// Copyright (c) 2006-2008 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_SYNC_SYNC_SETUP_WIZARD_H_
+#define CHROME_BROWSER_SYNC_SYNC_SETUP_WIZARD_H_
+
+#include "base/basictypes.h"
+
+#if defined(OS_LINUX)
+typedef struct _GtkWidget GtkWidget;
+typedef struct _GtkWindow GtkWindow;
+#else
+class SyncSetupFlowContainer;
+#endif
+
+class ProfileSyncService;
+
+class SyncSetupWizard {
+ public:
+ enum State {
+ // Show the Google Account login UI.
+ GAIA_LOGIN = 0,
+ // A login attempt succeeded. Depending on initial conditions, this may
+ // cause a transition to DONE, or to wait for an explicit transition (via
+ // Step) to the next state.
+ GAIA_SUCCESS,
+ // The user needs to accept a merge and sync warning to proceed.
+ MERGE_AND_SYNC,
+ // The panic switch. Something went terribly wrong during setup and we
+ // can't recover.
+ FATAL_ERROR,
+ // A final state for when setup completes and it is possible it is the
+ // user's first time (globally speaking) as the cloud doesn't have any
+ // bookmarks. We show additional info in this case to explain setting up
+ // more computers.
+ DONE_FIRST_TIME,
+ // A catch-all done case for any setup process.
+ DONE
+ };
+
+ explicit SyncSetupWizard(ProfileSyncService* service);
+ ~SyncSetupWizard();
+
+ // Advances the wizard to the specified state if possible, or opens a
+ // new dialog starting at |advance_state|. If the wizard has never ran
+ // through to completion, it will always attempt to do so. Otherwise, e.g
+ // for a transient auth failure, it will just run as far as is necessary
+ // based on |advance_state| (so for auth failure, up to GAIA_SUCCESS).
+ void Step(State advance_state);
+
+ // Whether or not a dialog is currently showing. Useful to determine
+ // if various buttons in the UI should be enabled or disabled.
+ bool IsVisible() const;
+
+#if defined(OS_LINUX)
+ void SetVisible(bool visible) { visible_ = visible; }
+#endif
+
+ private:
+ // If we just need to pop open an individual dialog, say to collect
+ // gaia credentials in the event of a steady-state auth failure, this is
+ // a "discrete" run (as in not a continuous wizard flow). This returns
+ // the end state to pass to Run for a given |start_state|.
+ static State GetEndStateForDiscreteRun(State start_state);
+
+ // Helper to return whether |state| warrants starting a new flow.
+ static bool IsTerminalState(State state);
+
+ ProfileSyncService* service_;
+
+#if defined(OS_LINUX)
+ bool visible_;
+#else
+ SyncSetupFlowContainer* flow_container_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(SyncSetupWizard);
+};
+
+#endif // CHROME_BROWSER_SYNC_SYNC_SETUP_WIZARD_H_
+
diff --git a/chrome/browser/sync/sync_setup_wizard_unittest.cc b/chrome/browser/sync/sync_setup_wizard_unittest.cc
new file mode 100644
index 0000000..3a04746
--- /dev/null
+++ b/chrome/browser/sync/sync_setup_wizard_unittest.cc
@@ -0,0 +1,376 @@
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifdef CHROME_PERSONALIZATION
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/json_writer.h"
+#include "base/scoped_ptr.h"
+#include "base/stl_util-inl.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/sync_setup_flow.h"
+#include "chrome/browser/sync/sync_setup_wizard.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/test/browser_with_test_window_test.h"
+#include "chrome/test/testing_profile.h"
+#include "chrome/test/test_browser_window.h"
+
+static const char* kTestUser = "chrome.p13n.test@gmail.com";
+static const char* kTestPassword = "passwd";
+
+// A PSS subtype to inject.
+class ProfileSyncServiceForWizardTest : public ProfileSyncService {
+ public:
+ explicit ProfileSyncServiceForWizardTest(Profile* profile)
+ : ProfileSyncService(profile), user_accepted_merge_and_sync_(false),
+ user_cancelled_dialog_(false) {
+ RegisterPreferences();
+ }
+
+ virtual ~ProfileSyncServiceForWizardTest() { }
+
+ virtual void OnUserSubmittedAuth(const std::string& username,
+ const std::string& password) {
+ username_ = username;
+ password_ = password;
+ }
+ virtual void OnUserAcceptedMergeAndSync() {
+ user_accepted_merge_and_sync_ = true;
+ }
+ virtual void OnUserCancelledDialog() {
+ user_cancelled_dialog_ = true;
+ }
+
+ virtual string16 GetAuthenticatedUsername() const {
+ return UTF8ToUTF16(username_);
+ }
+
+ void set_auth_state(const std::string& last_email, AuthErrorState state) {
+ last_attempted_user_email_ = last_email;
+ last_auth_error_ = state;
+ }
+
+ void ResetTestStats() {
+ username_.clear();
+ password_.clear();
+ user_accepted_merge_and_sync_ = false;
+ user_cancelled_dialog_ = false;
+ }
+
+ std::string username_;
+ std::string password_;
+ bool user_accepted_merge_and_sync_;
+ bool user_cancelled_dialog_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceForWizardTest);
+};
+
+class TestingProfileWithSyncService : public TestingProfile {
+ public:
+ TestingProfileWithSyncService() {
+ sync_service_.reset(new ProfileSyncServiceForWizardTest(this));
+ }
+
+ virtual ProfileSyncService* GetProfileSyncService() {
+ return sync_service_.get();
+ }
+ private:
+ scoped_ptr<ProfileSyncService> sync_service_;
+};
+
+class TestBrowserWindowForWizardTest : public TestBrowserWindow {
+ public:
+ explicit TestBrowserWindowForWizardTest(Browser* browser)
+ : TestBrowserWindow(browser), flow_(NULL),
+ was_show_html_dialog_called_(false) {
+ }
+
+ virtual ~TestBrowserWindowForWizardTest() {
+ if (flow_.get()) {
+ // In real life, the handlers are destroyed by the DOMUI infrastructure,
+ // which calls GetDOMMessageHandlers to take ownership. This does not
+ // exist in our test, so we perform cleanup manually.
+ std::vector<DOMMessageHandler*> handlers;
+ flow_->GetDOMMessageHandlers(&handlers);
+ // The handler contract is that they are valid for the lifetime of the
+ // HTMLDialogUIDelegate, but are cleaned up after the dialog is closed
+ // and/or deleted.
+ flow_.reset();
+ STLDeleteElements(&handlers);
+ }
+ }
+
+ // We intercept this call to hijack the flow created and then use it to
+ // drive the wizard as if we were the HTML page.
+ virtual void ShowHTMLDialog(HtmlDialogUIDelegate* delegate,
+ gfx::NativeWindow parent_window) {
+ flow_.reset(static_cast<SyncSetupFlow*>(delegate));
+ was_show_html_dialog_called_ = true;
+ }
+
+ bool TestAndResetWasShowHTMLDialogCalled() {
+ bool ret = was_show_html_dialog_called_;
+ was_show_html_dialog_called_ = false;
+ return ret;
+ }
+
+ // Simulates the user (or browser view hierarchy) closing the html dialog.
+ // Handles cleaning up the delegate and associated handlers.
+ void CloseDialog() {
+ if (flow_.get()) {
+ std::vector<DOMMessageHandler*> handlers;
+ flow_->GetDOMMessageHandlers(&handlers);
+ // The flow deletes itself here. Don't use reset().
+ flow_.release()->OnDialogClosed("");
+ STLDeleteElements(&handlers);
+ }
+ }
+
+ SyncSetupFlow* flow() { return flow_.get(); }
+
+ private:
+ // In real life, this is owned by the view that is opened by the browser. We
+ // mock all that out, so we need to take ownership so the flow doesn't leak.
+ scoped_ptr<SyncSetupFlow> flow_;
+
+ bool was_show_html_dialog_called_;
+};
+
+class SyncSetupWizardTest : public BrowserWithTestWindowTest {
+ public:
+ SyncSetupWizardTest() : test_window_(NULL), wizard_(NULL) { }
+ virtual ~SyncSetupWizardTest() { }
+ virtual void SetUp() {
+ set_profile(new TestingProfileWithSyncService());
+ profile()->CreateBookmarkModel(false);
+ // Wait for the bookmarks model to load.
+ profile()->BlockUntilBookmarkModelLoaded();
+ set_browser(new Browser(Browser::TYPE_NORMAL, profile()));
+ test_window_ = new TestBrowserWindowForWizardTest(browser());
+ set_window(test_window_);
+ browser()->set_window(window());
+ BrowserList::SetLastActive(browser());
+ service_ = static_cast<ProfileSyncServiceForWizardTest*>(
+ profile()->GetProfileSyncService());
+ wizard_.reset(new SyncSetupWizard(service_));
+ }
+
+ virtual void TearDown() {
+ test_window_ = NULL;
+ service_ = NULL;
+ wizard_.reset();
+ }
+
+ TestBrowserWindowForWizardTest* test_window_;
+ scoped_ptr<SyncSetupWizard> wizard_;
+ ProfileSyncServiceForWizardTest* service_;
+};
+
+TEST_F(SyncSetupWizardTest, InitialStepLogin) {
+ DictionaryValue dialog_args;
+ SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
+ std::string json_start_args;
+ JSONWriter::Write(&dialog_args, false, &json_start_args);
+ ListValue credentials;
+ std::string auth = "{\"user\":\"";
+ auth += std::string(kTestUser) + "\",\"pass\":\"";
+ auth += std::string(kTestPassword) + "\"}";
+ credentials.Append(new StringValue(auth));
+
+ EXPECT_FALSE(wizard_->IsVisible());
+ EXPECT_FALSE(test_window_->flow());
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+
+ EXPECT_TRUE(wizard_->IsVisible());
+ EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+ EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
+ EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->end_state_);
+ EXPECT_EQ(json_start_args, test_window_->flow()->dialog_start_args_);
+
+ // Simulate the user submitting credentials.
+ test_window_->flow()->flow_handler_->HandleSubmitAuth(&credentials);
+ EXPECT_TRUE(wizard_->IsVisible());
+ EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
+ EXPECT_EQ(kTestUser, service_->username_);
+ EXPECT_EQ(kTestPassword, service_->password_);
+ EXPECT_FALSE(service_->user_accepted_merge_and_sync_);
+ EXPECT_FALSE(service_->user_cancelled_dialog_);
+ service_->ResetTestStats();
+
+ // Simulate failed credentials.
+ service_->set_auth_state(kTestUser, AUTH_ERROR_INVALID_GAIA_CREDENTIALS);
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ EXPECT_TRUE(wizard_->IsVisible());
+ EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+ EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
+ dialog_args.Clear();
+ SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
+ EXPECT_TRUE(2 == dialog_args.GetSize());
+ std::string actual_user;
+ dialog_args.GetString(L"user", &actual_user);
+ EXPECT_EQ(kTestUser, actual_user);
+ int error = -1;
+ dialog_args.GetInteger(L"error", &error);
+ EXPECT_EQ(static_cast<int>(AUTH_ERROR_INVALID_GAIA_CREDENTIALS), error);
+ service_->set_auth_state(kTestUser, AUTH_ERROR_NONE);
+
+ // Simulate success.
+ wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
+ EXPECT_TRUE(wizard_->IsVisible());
+ EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+ EXPECT_EQ(SyncSetupWizard::GAIA_SUCCESS,
+ test_window_->flow()->current_state_);
+
+ wizard_->Step(SyncSetupWizard::DONE); // No merge and sync.
+ EXPECT_TRUE(wizard_->IsVisible());
+ EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+ EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->current_state_);
+}
+
+TEST_F(SyncSetupWizardTest, InitialStepMergeAndSync) {
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ EXPECT_TRUE(wizard_->IsVisible());
+ EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+ EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->end_state_);
+
+ wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
+ wizard_->Step(SyncSetupWizard::MERGE_AND_SYNC);
+ EXPECT_TRUE(wizard_->IsVisible());
+ EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+ EXPECT_EQ(SyncSetupWizard::MERGE_AND_SYNC,
+ test_window_->flow()->current_state_);
+
+ test_window_->flow()->flow_handler_->HandleSubmitMergeAndSync(NULL);
+ EXPECT_TRUE(wizard_->IsVisible());
+ EXPECT_EQ(SyncSetupWizard::MERGE_AND_SYNC,
+ test_window_->flow()->current_state_);
+ EXPECT_EQ(std::string(), service_->username_);
+ EXPECT_EQ(std::string(), service_->password_);
+ EXPECT_TRUE(service_->user_accepted_merge_and_sync_);
+ EXPECT_FALSE(service_->user_cancelled_dialog_);
+ service_->ResetTestStats();
+ wizard_->Step(SyncSetupWizard::DONE_FIRST_TIME); // No merge and sync.
+ EXPECT_TRUE(wizard_->IsVisible());
+ EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+ EXPECT_EQ(SyncSetupWizard::DONE_FIRST_TIME,
+ test_window_->flow()->current_state_);
+}
+
+TEST_F(SyncSetupWizardTest, DialogCancelled) {
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ // Simulate the user closing the dialog.
+ test_window_->CloseDialog();
+ EXPECT_FALSE(wizard_->IsVisible());
+ EXPECT_TRUE(service_->user_cancelled_dialog_);
+ EXPECT_EQ(std::string(), service_->username_);
+ EXPECT_EQ(std::string(), service_->password_);
+ EXPECT_FALSE(service_->user_accepted_merge_and_sync_);
+
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ EXPECT_TRUE(wizard_->IsVisible());
+ EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+
+ wizard_->Step(SyncSetupWizard::MERGE_AND_SYNC);
+ test_window_->CloseDialog();
+ EXPECT_FALSE(wizard_->IsVisible());
+ EXPECT_TRUE(service_->user_cancelled_dialog_);
+ EXPECT_EQ(std::string(), service_->username_);
+ EXPECT_EQ(std::string(), service_->password_);
+ EXPECT_FALSE(service_->user_accepted_merge_and_sync_);
+}
+
+TEST_F(SyncSetupWizardTest, InvalidTransitions) {
+ wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
+ EXPECT_FALSE(wizard_->IsVisible());
+ EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+
+ wizard_->Step(SyncSetupWizard::DONE);
+ EXPECT_FALSE(wizard_->IsVisible());
+ EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+
+ wizard_->Step(SyncSetupWizard::DONE_FIRST_TIME);
+ EXPECT_FALSE(wizard_->IsVisible());
+ EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ wizard_->Step(SyncSetupWizard::MERGE_AND_SYNC);
+ EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
+
+ wizard_->Step(SyncSetupWizard::DONE);
+ EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
+ wizard_->Step(SyncSetupWizard::DONE_FIRST_TIME);
+ EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_);
+
+ wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
+ wizard_->Step(SyncSetupWizard::MERGE_AND_SYNC);
+ EXPECT_EQ(SyncSetupWizard::MERGE_AND_SYNC,
+ test_window_->flow()->current_state_);
+
+ wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
+ EXPECT_EQ(SyncSetupWizard::MERGE_AND_SYNC,
+ test_window_->flow()->current_state_);
+
+ wizard_->Step(SyncSetupWizard::FATAL_ERROR);
+ EXPECT_EQ(SyncSetupWizard::FATAL_ERROR, test_window_->flow()->current_state_);
+}
+
+TEST_F(SyncSetupWizardTest, FullSuccessfulRunSetsPref) {
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
+ wizard_->Step(SyncSetupWizard::MERGE_AND_SYNC);
+ wizard_->Step(SyncSetupWizard::DONE);
+ test_window_->CloseDialog();
+ EXPECT_FALSE(wizard_->IsVisible());
+ EXPECT_TRUE(service_->profile()->GetPrefs()->GetBoolean(
+ prefs::kSyncHasSetupCompleted));
+}
+
+TEST_F(SyncSetupWizardTest, FirstFullSuccessfulRunSetsPref) {
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
+ wizard_->Step(SyncSetupWizard::MERGE_AND_SYNC);
+ wizard_->Step(SyncSetupWizard::DONE_FIRST_TIME);
+ test_window_->CloseDialog();
+ EXPECT_FALSE(wizard_->IsVisible());
+ EXPECT_TRUE(service_->profile()->GetPrefs()->GetBoolean(
+ prefs::kSyncHasSetupCompleted));
+}
+
+TEST_F(SyncSetupWizardTest, DiscreteRun) {
+ DictionaryValue dialog_args;
+ // For a discrete run, we need to have ran through setup once.
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
+ wizard_->Step(SyncSetupWizard::MERGE_AND_SYNC);
+ wizard_->Step(SyncSetupWizard::DONE);
+ test_window_->CloseDialog();
+ EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ EXPECT_EQ(SyncSetupWizard::GAIA_SUCCESS, test_window_->flow()->end_state_);
+
+ service_->set_auth_state(kTestUser, AUTH_ERROR_INVALID_GAIA_CREDENTIALS);
+ wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ EXPECT_TRUE(wizard_->IsVisible());
+ SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
+ EXPECT_TRUE(2 == dialog_args.GetSize());
+ std::string actual_user;
+ dialog_args.GetString(L"user", &actual_user);
+ EXPECT_EQ(kTestUser, actual_user);
+ int error = -1;
+ dialog_args.GetInteger(L"error", &error);
+ EXPECT_EQ(static_cast<int>(AUTH_ERROR_INVALID_GAIA_CREDENTIALS), error);
+ service_->set_auth_state(kTestUser, AUTH_ERROR_NONE);
+
+ wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
+ EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled());
+}
+
+#endif // CHROME_PERSONALIZATION
diff --git a/chrome/browser/sync/util/character_set_converters.h b/chrome/browser/sync/util/character_set_converters.h
index 1db32c7..6a833ee 100644
--- a/chrome/browser/sync/util/character_set_converters.h
+++ b/chrome/browser/sync/util/character_set_converters.h
@@ -136,6 +136,21 @@ inline void AppendPathStringToUTF8(const PathString& wide,
return AppendPathStringToUTF8(wide.data(), wide.length(), output_string);
}
+// Versions of UTF8ToPathString/PathStringToUTF8 that return the converted
+// string directly. Any errors encountered will CHECK(). These functions are
+// intended to be used only for testing.
+
+inline PathString UTF8ToPathStringQuick(const std::string &utf8) {
+ PathString wide;
+ CHECK(UTF8ToPathString(utf8.data(), utf8.size(), &wide));
+ return wide;
+}
+
+inline std::string PathStringToUTF8Quick(const PathString& wide) {
+ std::string utf8;
+ PathStringToUTF8(wide.data(), wide.size(), &utf8);
+ return utf8;
+}
inline bool Append(const PathChar* wide, int size,
std::string* output_string) {
diff --git a/chrome/browser/sync/util/user_settings_unittest.cc b/chrome/browser/sync/util/user_settings_unittest.cc
index 952c86b..02f3fd8 100644
--- a/chrome/browser/sync/util/user_settings_unittest.cc
+++ b/chrome/browser/sync/util/user_settings_unittest.cc
@@ -2,9 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE entry.
+#include <string>
+
#include "base/file_util.h"
#include "base/test/test_file_util.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/util/character_set_converters.h"
#include "chrome/browser/sync/util/user_settings.h"
#include "chrome/browser/sync/util/query_helpers.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -40,15 +43,10 @@ class UserSettingsTest : public testing::Test {
ExecOrDie(primer_handle, "CREATE TABLE shares"
" (email, share_name, file_name,"
" PRIMARY KEY(email, share_name) ON CONFLICT REPLACE)");
-#if OS_WIN
- // Populate a share.
- ExecOrDie(primer_handle, "INSERT INTO shares values ( ?, ?, ?)",
- "foo@foo.com", "foo@foo.com", WideToUTF8(kOldStyleSyncDataDB));
-#elif OS_LINUX
// Populate a share.
ExecOrDie(primer_handle, "INSERT INTO shares values ( ?, ?, ?)",
- "foo@foo.com", "foo@foo.com", kOldStyleSyncDataDB);
-#endif
+ "foo@foo.com", "foo@foo.com",
+ browser_sync::PathStringToUTF8Quick(kOldStyleSyncDataDB));
sqlite3_close(primer_handle);
}