diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-16 06:48:32 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-16 06:48:32 +0000 |
commit | 109763bb929004fefa409c342d1c14a78bfe126f (patch) | |
tree | 0614c618674684955caa470ee49d26e05e7dc3e1 /chrome/browser/sync | |
parent | 457764525fd9a27612d7c8bc9d77ae0646db1a56 (diff) | |
download | chromium_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.cc | 2 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_thread.cc | 2 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service.h | 2 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_flow.cc | 304 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_flow.h | 189 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_wizard.cc | 199 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_wizard.h | 82 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_wizard_unittest.cc | 376 | ||||
-rw-r--r-- | chrome/browser/sync/util/character_set_converters.h | 15 | ||||
-rw-r--r-- | chrome/browser/sync/util/user_settings_unittest.cc | 12 |
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); } |