summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views/sync
diff options
context:
space:
mode:
authortim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-05 01:18:27 +0000
committertim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-05 01:18:27 +0000
commit132c8565a63ad57d680b6b8d9beaa28786a46ea8 (patch)
tree428425605547842abd6a7ab851bd27132f11efde /chrome/browser/views/sync
parentf902600776feea6570c876f2349bb4a8746ea95b (diff)
downloadchromium_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.cc240
-rw-r--r--chrome/browser/views/sync/sync_setup_flow.h176
-rw-r--r--chrome/browser/views/sync/sync_setup_wizard.cc57
-rw-r--r--chrome/browser/views/sync/sync_setup_wizard.h50
-rw-r--r--chrome/browser/views/sync/sync_setup_wizard_unittest.cc329
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