diff options
author | tim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-05 01:18:27 +0000 |
---|---|---|
committer | tim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-05 01:18:27 +0000 |
commit | 132c8565a63ad57d680b6b8d9beaa28786a46ea8 (patch) | |
tree | 428425605547842abd6a7ab851bd27132f11efde /chrome/browser/views/sync | |
parent | f902600776feea6570c876f2349bb4a8746ea95b (diff) | |
download | chromium_src-132c8565a63ad57d680b6b8d9beaa28786a46ea8.zip chromium_src-132c8565a63ad57d680b6b8d9beaa28786a46ea8.tar.gz chromium_src-132c8565a63ad57d680b6b8d9beaa28786a46ea8.tar.bz2 |
Add files to browser/sync and tweak includes.
Create browser/sync/glue and /engine.
Create sync watchlist and add a few folks.
No GYP change here so no build changes should occur.
chrome.gyp CL is coming shortly, as well as live_sync tests.
Review URL: http://codereview.chromium.org/160598
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22454 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/views/sync')
-rw-r--r-- | chrome/browser/views/sync/sync_setup_flow.cc | 240 | ||||
-rw-r--r-- | chrome/browser/views/sync/sync_setup_flow.h | 176 | ||||
-rw-r--r-- | chrome/browser/views/sync/sync_setup_wizard.cc | 57 | ||||
-rw-r--r-- | chrome/browser/views/sync/sync_setup_wizard.h | 50 | ||||
-rw-r--r-- | chrome/browser/views/sync/sync_setup_wizard_unittest.cc | 329 |
5 files changed, 852 insertions, 0 deletions
diff --git a/chrome/browser/views/sync/sync_setup_flow.cc b/chrome/browser/views/sync/sync_setup_flow.cc new file mode 100644 index 0000000..5df1a33 --- /dev/null +++ b/chrome/browser/views/sync/sync_setup_flow.cc @@ -0,0 +1,240 @@ +// 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 "base/json_reader.h" +#include "base/json_writer.h" +#include "base/string_util.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/browser/views/sync/sync_setup_flow.h" +#include "chrome/common/pref_service.h" + +static const int kSyncDialogWidth = 267; +static const int kSyncDialogHeight = 369; + +// XPath expression for finding specific iframes. +static const wchar_t* kLoginIFrameXPath = L"//iframe[@id='login']"; +static const wchar_t* kMergeIFrameXPath = L"//iframe[@id='merge']"; + +// 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::ShowMergeAndSyncDone() { + ExecuteJavascriptInIFrame(kMergeIFrameXPath, L"showMergeAndSyncDone();"); +} + +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 { + size->set_width(kSyncDialogWidth); + size->set_height(kSyncDialogHeight); +} + +// 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) { + PrefService* prefs = service_->profile()->GetPrefs(); + prefs->SetBoolean(prefs::kSyncHasSetupCompleted, true); + prefs->ScheduleSavePersistentPrefs(); + } + 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::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::DONE: + if (current_state_ == SyncSetupWizard::MERGE_AND_SYNC) + flow_handler_->ShowMergeAndSyncDone(); + else if (current_state_ == SyncSetupWizard::GAIA_SUCCESS) + flow_handler_->ShowGaiaSuccessAndClose(); + 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/views/sync/sync_setup_flow.h b/chrome/browser/views/sync/sync_setup_flow.h new file mode 100644 index 0000000..ff86f7b --- /dev/null +++ b/chrome/browser/views/sync/sync_setup_flow.h @@ -0,0 +1,176 @@ +// 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_VIEWS_SYNC_SYNC_SETUP_FLOW_H_ +#define CHROME_BROWSER_VIEWS_SYNC_SYNC_SETUP_FLOW_H_ + +#include <string> +#include <vector> + +#include "chrome/browser/dom_ui/html_dialog_ui.h" +#include "chrome/browser/sync/personalization_strings.h" +#include "chrome/browser/views/sync/sync_setup_wizard.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("cloudy://resources/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 kLoginDialogTitle; + } + + // 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), + 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. + + // 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 ShowMergeAndSyncDone(); + + 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_VIEWS_SYNC_SYNC_SETUP_FLOW_H_ +#endif // CHROME_PERSONALIZATION diff --git a/chrome/browser/views/sync/sync_setup_wizard.cc b/chrome/browser/views/sync/sync_setup_wizard.cc new file mode 100644 index 0000000..18ef7b8 --- /dev/null +++ b/chrome/browser/views/sync/sync_setup_wizard.cc @@ -0,0 +1,57 @@ +// 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/views/sync/sync_setup_wizard.h" + +#include "chrome/common/pref_service.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/views/sync/sync_setup_flow.h" + +SyncSetupWizard::SyncSetupWizard(ProfileSyncService* service) + : service_(service), flow_container_(new SyncSetupFlowContainer()) { +} + +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 (advance_state == DONE || advance_state == GAIA_SUCCESS) + 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 (advance_state == DONE || advance_state == GAIA_SUCCESS) + return; // Nothing to do. + flow_container_->set_flow(SyncSetupFlow::Run(service_, flow_container_, + advance_state, GetEndStateForDiscreteRun(advance_state))); + } +} + +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/views/sync/sync_setup_wizard.h b/chrome/browser/views/sync/sync_setup_wizard.h new file mode 100644 index 0000000..7de7253 --- /dev/null +++ b/chrome/browser/views/sync/sync_setup_wizard.h @@ -0,0 +1,50 @@ +// 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_VIEWS_SYNC_SYNC_SETUP_WIZARD_H_ +#define CHROME_BROWSER_VIEWS_SYNC_SYNC_SETUP_WIZARD_H_ + +#include "base/basictypes.h" + +class SyncSetupFlowContainer; + +class ProfileSyncService; + +class SyncSetupWizard { + public: + enum State { + GAIA_LOGIN = 0, + GAIA_SUCCESS, + MERGE_AND_SYNC, + 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; + + 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); + + ProfileSyncService* service_; + SyncSetupFlowContainer* flow_container_; + + DISALLOW_COPY_AND_ASSIGN(SyncSetupWizard); +}; + +#endif // CHROME_BROWSER_VIEWS_SYNC_SYNC_SETUP_WIZARD_H_ diff --git a/chrome/browser/views/sync/sync_setup_wizard_unittest.cc b/chrome/browser/views/sync/sync_setup_wizard_unittest.cc new file mode 100644 index 0000000..bfdfd73 --- /dev/null +++ b/chrome/browser/views/sync/sync_setup_wizard_unittest.cc @@ -0,0 +1,329 @@ +// 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 "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/sync/personalization.h" +#include "chrome/browser/sync/profile_sync_service.h" +#include "chrome/browser/views/sync/sync_setup_flow.h" +#include "chrome/browser/views/sync/sync_setup_wizard.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 = "g00gl3g00gl3"; + +// 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) { + } + + 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 UTF8ToWide(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 ProfilePersonalizationTestImpl : public ProfilePersonalization { + public: + explicit ProfilePersonalizationTestImpl(Profile* p) + : sync_service_(new ProfileSyncServiceForWizardTest(p)) { + } + virtual ProfileSyncService* sync_service() { return sync_service_.get(); } + private: + scoped_ptr<ProfileSyncService> sync_service_; +}; + +class TestingProfileWithPersonalization : public TestingProfile { + public: + TestingProfileWithPersonalization() { + personalization_.reset(new ProfilePersonalizationTestImpl(this)); + } + + virtual ProfilePersonalization* GetProfilePersonalization() { + return personalization_.get(); + } + private: + scoped_ptr<ProfilePersonalization> personalization_; +}; + +class TestBrowserWindowForWizardTest : public TestBrowserWindow { + public: + explicit TestBrowserWindowForWizardTest(Browser* browser) + : TestBrowserWindow(browser), flow_(NULL), + was_show_html_dialog_called_(false) { + } + + // 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_ = 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; + } + + SyncSetupFlow* flow_; + private: + 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 TestingProfileWithPersonalization()); + profile()->CreateBookmarkModel(false); + // Wait for the bookmarks model to load. + profile()->BlockUntilBookmarkModelLoaded(); + profile()->GetPrefs()->RegisterBooleanPref(prefs::kSyncHasSetupCompleted, + false); + 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()->GetProfilePersonalization()->sync_service()); + 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_EQ(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); // 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, DialogCancelled) { + wizard_->Step(SyncSetupWizard::GAIA_LOGIN); + test_window_->flow_->OnDialogClosed(""); + 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_->flow_->OnDialogClosed(""); + 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::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::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_); +} + +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_->flow_->OnDialogClosed(""); + 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_->flow_->OnDialogClosed(""); + 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_EQ(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 |