// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/sync/sync_setup_wizard.h" #include "base/memory/singleton.h" #include "base/message_loop.h" #include "chrome/browser/google/google_util.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/sync_setup_flow.h" #include "chrome/browser/ui/webui/chrome_url_data_manager.h" #include "chrome/common/jstemplate_builder.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "content/browser/browser_thread.h" #include "googleurl/src/gurl.h" #include "grit/app_resources.h" #include "grit/browser_resources.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "grit/locale_settings.h" #include "ui/base/resource/resource_bundle.h" namespace { // Utility method to keep dictionary population code streamlined. void AddString(DictionaryValue* dictionary, const std::string& key, int resource_id) { dictionary->SetString(key, l10n_util::GetStringUTF16(resource_id)); } } class SyncResourcesSource : public ChromeURLDataManager::DataSource { public: SyncResourcesSource() : DataSource(chrome::kChromeUISyncResourcesHost, MessageLoop::current()) { } virtual void StartDataRequest(const std::string& path, bool is_incognito, int request_id); virtual std::string GetMimeType(const std::string& path) const { return "text/html"; } static const char* kInvalidPasswordHelpUrl; static const char* kCanNotAccessAccountUrl; static const char* kCreateNewAccountUrl; static const char* kEncryptionHelpUrl; private: virtual ~SyncResourcesSource() {} // Takes a string containing an URL and returns an URL containing a CGI // parameter of the form "&hl=xy" where 'xy' is the language code of the // current locale. std::string GetLocalizedUrl(const std::string& url) const; DISALLOW_COPY_AND_ASSIGN(SyncResourcesSource); }; const char* SyncResourcesSource::kInvalidPasswordHelpUrl = "http://www.google.com/support/accounts/bin/answer.py?ctx=ch&answer=27444"; const char* SyncResourcesSource::kCanNotAccessAccountUrl = "http://www.google.com/support/accounts/bin/answer.py?answer=48598"; #if defined(OS_CHROMEOS) const char* SyncResourcesSource::kEncryptionHelpUrl = "http://www.google.com/support/chromeos/bin/answer.py?answer=1181035"; #else const char* SyncResourcesSource::kEncryptionHelpUrl = "http://www.google.com/support/chrome/bin/answer.py?answer=1181035"; #endif const char* SyncResourcesSource::kCreateNewAccountUrl = "https://www.google.com/accounts/NewAccount?service=chromiumsync"; void SyncResourcesSource::StartDataRequest(const std::string& path_raw, bool is_incognito, int request_id) { using l10n_util::GetStringUTF16; using l10n_util::GetStringFUTF16; const char kSyncSetupFlowPath[] = "setup"; const char kSyncGaiaLoginPath[] = "gaialogin"; const char kSyncConfigurePath[] = "configure"; const char kSyncPassphrasePath[] = "passphrase"; const char kSyncFirstPassphrasePath[] = "firstpassphrase"; const char kSyncSettingUpPath[] = "settingup"; const char kSyncSetupDonePath[] = "setupdone"; std::string response; DictionaryValue strings; DictionaryValue* dict = &strings; int html_resource_id = 0; if (path_raw == kSyncGaiaLoginPath) { html_resource_id = IDR_GAIA_LOGIN_HTML; // Start by setting the per-locale URLs we show on the setup wizard. dict->SetString("invalidpasswordhelpurl", GetLocalizedUrl(kInvalidPasswordHelpUrl)); dict->SetString("cannotaccessaccounturl", GetLocalizedUrl(kCanNotAccessAccountUrl)); dict->SetString("createnewaccounturl", GetLocalizedUrl(kCreateNewAccountUrl)); AddString(dict, "settingupsync", IDS_SYNC_LOGIN_SETTING_UP_SYNC); dict->SetString("introduction", GetStringFUTF16(IDS_SYNC_LOGIN_INTRODUCTION, GetStringUTF16(IDS_PRODUCT_NAME))); AddString(dict, "signinprefix", IDS_SYNC_LOGIN_SIGNIN_PREFIX); AddString(dict, "signinsuffix", IDS_SYNC_LOGIN_SIGNIN_SUFFIX); AddString(dict, "cannotbeblank", IDS_SYNC_CANNOT_BE_BLANK); AddString(dict, "emaillabel", IDS_SYNC_LOGIN_EMAIL); AddString(dict, "passwordlabel", IDS_SYNC_LOGIN_PASSWORD); AddString(dict, "invalidcredentials", IDS_SYNC_INVALID_USER_CREDENTIALS); AddString(dict, "signin", IDS_SYNC_SIGNIN); AddString(dict, "couldnotconnect", IDS_SYNC_LOGIN_COULD_NOT_CONNECT); AddString(dict, "cannotaccessaccount", IDS_SYNC_CANNOT_ACCESS_ACCOUNT); AddString(dict, "createaccount", IDS_SYNC_CREATE_ACCOUNT); AddString(dict, "cancel", IDS_CANCEL); AddString(dict, "settingup", IDS_SYNC_LOGIN_SETTING_UP); AddString(dict, "success", IDS_SYNC_SUCCESS); AddString(dict, "errorsigningin", IDS_SYNC_ERROR_SIGNING_IN); AddString(dict, "captchainstructions", IDS_SYNC_GAIA_CAPTCHA_INSTRUCTIONS); AddString(dict, "invalidaccesscode", IDS_SYNC_INVALID_ACCESS_CODE_LABEL); AddString(dict, "enteraccesscode", IDS_SYNC_ENTER_ACCESS_CODE_LABEL); AddString(dict, "getaccesscodehelp", IDS_SYNC_ACCESS_CODE_HELP_LABEL); AddString(dict, "getaccesscodeurl", IDS_SYNC_GET_ACCESS_CODE_URL); } else if (path_raw == kSyncConfigurePath) { html_resource_id = IDR_SYNC_CONFIGURE_HTML; AddString(dict, "dataTypes", IDS_SYNC_DATA_TYPES_TAB_NAME); AddString(dict, "encryption", IDS_SYNC_ENCRYPTION_TAB_NAME); // Stuff for the choose data types localized. AddString(dict, "choosedatatypesheader", IDS_SYNC_CHOOSE_DATATYPES_HEADER); dict->SetString("choosedatatypesinstructions", GetStringFUTF16(IDS_SYNC_CHOOSE_DATATYPES_INSTRUCTIONS, GetStringUTF16(IDS_PRODUCT_NAME))); AddString(dict, "keepeverythingsynced", IDS_SYNC_EVERYTHING); AddString(dict, "choosedatatypes", IDS_SYNC_CHOOSE_DATATYPES); AddString(dict, "bookmarks", IDS_SYNC_DATATYPE_BOOKMARKS); AddString(dict, "preferences", IDS_SYNC_DATATYPE_PREFERENCES); AddString(dict, "autofill", IDS_SYNC_DATATYPE_AUTOFILL); AddString(dict, "themes", IDS_SYNC_DATATYPE_THEMES); AddString(dict, "passwords", IDS_SYNC_DATATYPE_PASSWORDS); AddString(dict, "extensions", IDS_SYNC_DATATYPE_EXTENSIONS); AddString(dict, "typedurls", IDS_SYNC_DATATYPE_TYPED_URLS); AddString(dict, "apps", IDS_SYNC_DATATYPE_APPS); AddString(dict, "foreignsessions", IDS_SYNC_DATATYPE_SESSIONS); AddString(dict, "synczerodatatypeserror", IDS_SYNC_ZERO_DATA_TYPES_ERROR); AddString(dict, "abortederror", IDS_SYNC_SETUP_ABORTED_BY_PENDING_CLEAR); // Stuff for the encryption tab. dict->SetString("encryptionInstructions", GetStringFUTF16(IDS_SYNC_ENCRYPTION_INSTRUCTIONS, GetStringUTF16(IDS_PRODUCT_NAME))); AddString(dict, "encryptAllLabel", IDS_SYNC_ENCRYPT_ALL_LABEL); AddString(dict, "googleOption", IDS_SYNC_PASSPHRASE_OPT_GOOGLE); AddString(dict, "explicitOption", IDS_SYNC_PASSPHRASE_OPT_EXPLICIT); AddString(dict, "sectionGoogleMessage", IDS_SYNC_PASSPHRASE_MSG_GOOGLE); AddString(dict, "sectionExplicitMessage", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT); AddString(dict, "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL); AddString(dict, "confirmLabel", IDS_SYNC_CONFIRM_PASSPHRASE_LABEL); AddString(dict, "emptyErrorMessage", IDS_SYNC_EMPTY_PASSPHRASE_ERROR); AddString(dict, "mismatchErrorMessage", IDS_SYNC_PASSPHRASE_MISMATCH_ERROR); AddString(dict, "passphraseWarning", IDS_SYNC_PASSPHRASE_WARNING); AddString(dict, "cleardata", IDS_SYNC_CLEAR_DATA_FOR_PASSPHRASE); AddString(dict, "cleardatalink", IDS_SYNC_CLEAR_DATA_LINK); AddString(dict, "learnmore", IDS_LEARN_MORE); dict->SetString("encryptionhelpurl", GetLocalizedUrl(kEncryptionHelpUrl)); // Stuff for the footer. AddString(dict, "ok", IDS_OK); AddString(dict, "cancel", IDS_CANCEL); } else if (path_raw == kSyncPassphrasePath) { html_resource_id = IDR_SYNC_PASSPHRASE_HTML; AddString(dict, "enterPassphraseTitle", IDS_SYNC_ENTER_PASSPHRASE_TITLE); AddString(dict, "enterPassphraseBody", IDS_SYNC_ENTER_PASSPHRASE_BODY); AddString(dict, "enterOtherPassphraseBody", IDS_SYNC_ENTER_OTHER_PASSPHRASE_BODY); AddString(dict, "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL); AddString(dict, "incorrectPassphrase", IDS_SYNC_INCORRECT_PASSPHRASE); AddString(dict, "passphraseRecover", IDS_SYNC_PASSPHRASE_RECOVER); AddString(dict, "passphraseWarning", IDS_SYNC_PASSPHRASE_WARNING); AddString(dict, "cleardatalink", IDS_SYNC_CLEAR_DATA_LINK); AddString(dict, "cancelWarningHeader", IDS_SYNC_PASSPHRASE_CANCEL_WARNING_HEADER); AddString(dict, "cancelWarning", IDS_SYNC_PASSPHRASE_CANCEL_WARNING); AddString(dict, "yes", IDS_SYNC_PASSPHRASE_CANCEL_YES); AddString(dict, "no", IDS_SYNC_PASSPHRASE_CANCEL_NO); AddString(dict, "ok", IDS_OK); AddString(dict, "cancel", IDS_CANCEL); } else if (path_raw == kSyncFirstPassphrasePath) { html_resource_id = IDR_SYNC_FIRST_PASSPHRASE_HTML; AddString(dict, "title", IDS_SYNC_FIRST_PASSPHRASE_TITLE); dict->SetString("instructions", GetStringFUTF16(IDS_SYNC_FIRST_PASSPHRASE_MESSAGE, GetStringUTF16(IDS_PRODUCT_NAME))); AddString(dict, "googleOption", IDS_SYNC_PASSPHRASE_OPT_GOOGLE); AddString(dict, "explicitOption", IDS_SYNC_PASSPHRASE_OPT_EXPLICIT); AddString(dict, "sectionGoogleMessage", IDS_SYNC_PASSPHRASE_MSG_GOOGLE); AddString(dict, "sectionExplicitMessage", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT); AddString(dict, "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL); AddString(dict, "confirmLabel", IDS_SYNC_CONFIRM_PASSPHRASE_LABEL); AddString(dict, "emptyErrorMessage", IDS_SYNC_EMPTY_PASSPHRASE_ERROR); AddString(dict, "mismatchErrorMessage", IDS_SYNC_PASSPHRASE_MISMATCH_ERROR); AddString(dict, "learnmore", IDS_LEARN_MORE); dict->SetString("encryptionhelpurl", GetLocalizedUrl(kEncryptionHelpUrl)); AddString(dict, "syncpasswords", IDS_SYNC_FIRST_PASSPHRASE_OK); AddString(dict, "nothanks", IDS_SYNC_FIRST_PASSPHRASE_CANCEL); } else if (path_raw == kSyncSettingUpPath) { html_resource_id = IDR_SYNC_SETTING_UP_HTML; AddString(dict, "settingup", IDS_SYNC_LOGIN_SETTING_UP); AddString(dict, "cancel", IDS_CANCEL); } else if (path_raw == kSyncSetupDonePath) { html_resource_id = IDR_SYNC_SETUP_DONE_HTML; AddString(dict, "success", IDS_SYNC_SUCCESS); dict->SetString("setupsummary", GetStringFUTF16(IDS_SYNC_SETUP_ALL_DONE, GetStringUTF16(IDS_PRODUCT_NAME))); AddString(dict, "firsttimesummary", IDS_SYNC_SETUP_FIRST_TIME_ALL_DONE); AddString(dict, "okay", IDS_SYNC_SETUP_OK_BUTTON_LABEL); } else if (path_raw == kSyncSetupFlowPath) { html_resource_id = IDR_SYNC_SETUP_FLOW_HTML; } if (html_resource_id > 0) { const base::StringPiece html( ResourceBundle::GetSharedInstance().GetRawDataResource( html_resource_id)); SetFontAndTextDirection(dict); response = jstemplate_builder::GetI18nTemplateHtml(html, dict); } // Send the response. scoped_refptr html_bytes(new RefCountedBytes); html_bytes->data.resize(response.size()); std::copy(response.begin(), response.end(), html_bytes->data.begin()); SendResponse(request_id, html_bytes); } std::string SyncResourcesSource::GetLocalizedUrl( const std::string& url) const { GURL original_url(url); DCHECK(original_url.is_valid()); GURL localized_url = google_util::AppendGoogleLocaleParam(original_url); return localized_url.spec(); } SyncSetupWizard::SyncSetupWizard(ProfileSyncService* service) : service_(service), flow_container_(new SyncSetupFlowContainer()), parent_window_(NULL) { // If we're in a unit test, we may not have an IO thread or profile. Avoid // creating a SyncResourcesSource since we may leak it (since it's // DeleteOnUIThread). if (BrowserThread::IsMessageLoopValid(BrowserThread::IO) && service_->profile()) { // Add our network layer data source for 'cloudy' URLs. SyncResourcesSource* sync_source = new SyncResourcesSource(); service_->profile()->GetChromeURLDataManager()->AddDataSource(sync_source); } } 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, parent_window_)); } 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), parent_window_)); } } // static bool SyncSetupWizard::IsTerminalState(State advance_state) { return advance_state == GAIA_SUCCESS || advance_state == DONE || advance_state == DONE_FIRST_TIME || advance_state == FATAL_ERROR || advance_state == SETUP_ABORTED_BY_PENDING_CLEAR; } bool SyncSetupWizard::IsVisible() const { return flow_container_->get_flow() != NULL; } void SyncSetupWizard::Focus() { SyncSetupFlow* flow = flow_container_->get_flow(); if (flow) { flow->Focus(); } } void SyncSetupWizard::SetParent(gfx::NativeWindow parent_window) { parent_window_ = parent_window; } // static SyncSetupWizard::State SyncSetupWizard::GetEndStateForDiscreteRun( State start_state) { State result = FATAL_ERROR; if (start_state == GAIA_LOGIN) { result = GAIA_SUCCESS; } else if (start_state == ENTER_PASSPHRASE || start_state == CONFIGURE || start_state == PASSPHRASE_MIGRATION) { result = DONE; } DCHECK_NE(FATAL_ERROR, result) << "Invalid start state for discrete run: " << start_state; return result; }