diff options
author | johnnyg@chromium.org <johnnyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-08 01:45:18 +0000 |
---|---|---|
committer | johnnyg@chromium.org <johnnyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-08 01:45:18 +0000 |
commit | 22d3eff8c7ed0fe79450dc214857286e88c5dfed (patch) | |
tree | 7e2f37cf354aa0947d227e277ed3457aff670d6e /chrome/browser/sync | |
parent | b15b5e32959926441c298a89c843293c52de75e2 (diff) | |
download | chromium_src-22d3eff8c7ed0fe79450dc214857286e88c5dfed.zip chromium_src-22d3eff8c7ed0fe79450dc214857286e88c5dfed.tar.gz chromium_src-22d3eff8c7ed0fe79450dc214857286e88c5dfed.tar.bz2 |
Add passphrase migration UI according to the flow chart for users who require a passphrase the first time.
BUG=63014,58935
TEST=test
Review URL: http://codereview.chromium.org/5563004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70825 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/sync')
-rw-r--r-- | chrome/browser/sync/profile_sync_service.cc | 55 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service.h | 10 | ||||
-rw-r--r-- | chrome/browser/sync/resources/configure.html | 199 | ||||
-rw-r--r-- | chrome/browser/sync/resources/encryption_login.html | 6 | ||||
-rw-r--r-- | chrome/browser/sync/resources/firstpassphrase.html | 204 | ||||
-rw-r--r-- | chrome/browser/sync/resources/setup_flow.html | 7 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_flow.cc | 139 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_flow.h | 14 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_wizard.cc | 42 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_wizard.h | 5 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_wizard_unittest.cc | 17 | ||||
-rw-r--r-- | chrome/browser/sync/sync_ui_util.cc | 40 | ||||
-rw-r--r-- | chrome/browser/sync/sync_ui_util.h | 5 |
13 files changed, 633 insertions, 110 deletions
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc index a3da689..852ed4b 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -19,6 +19,7 @@ #include "base/string_util.h" #include "base/task.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/browser_signin.h" #include "chrome/browser/history/history_types.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/prefs/pref_service.h" @@ -31,6 +32,7 @@ #include "chrome/browser/sync/glue/session_data_type_controller.h" #include "chrome/browser/sync/profile_sync_factory.h" #include "chrome/browser/sync/signin_manager.h" +#include "chrome/browser/sync/sync_ui_util.h" #include "chrome/browser/sync/token_migrator.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/net/gaia/gaia_constants.h" @@ -39,6 +41,7 @@ #include "chrome/common/notification_type.h" #include "chrome/common/pref_names.h" #include "chrome/common/time_format.h" +#include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "jingle/notifier/communicator/const_communicator.h" #include "net/base/cookie_monster.h" @@ -716,11 +719,7 @@ void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) { } wizard_.SetParent(parent_window); - // This method will also be called if a passphrase is needed. - if (observed_passphrase_required_) - wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE); - else - wizard_.Step(SyncSetupWizard::GAIA_LOGIN); + wizard_.Step(SyncSetupWizard::GAIA_LOGIN); FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); } @@ -734,6 +733,31 @@ void ProfileSyncService::ShowConfigure(gfx::NativeWindow parent_window) { wizard_.Step(SyncSetupWizard::CONFIGURE); } +void ProfileSyncService::PromptForExistingPassphrase( + gfx::NativeWindow parent_window) { + if (WizardIsVisible()) { + wizard_.Focus(); + return; + } + wizard_.SetParent(parent_window); + wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE); +} + +void ProfileSyncService::ShowPassphraseMigration( + gfx::NativeWindow parent_window) { + wizard_.SetParent(parent_window); + wizard_.Step(SyncSetupWizard::PASSPHRASE_MIGRATION); +} + +void ProfileSyncService::SigninForPassphrase(TabContents* container) { + string16 prefilled_username = GetAuthenticatedUsername(); + string16 login_message = sync_ui_util::GetLoginMessageForEncryption(); + profile_->GetBrowserSignin()->RequestSignin(container, + prefilled_username, + login_message, + this); +} + SyncBackendHost::StatusSummary ProfileSyncService::QuerySyncStatusSummary() { if (backend_.get() && backend_initialized_) return backend_->GetStatusSummary(); @@ -1138,6 +1162,27 @@ void ProfileSyncService::Observe(NotificationType type, } } +// This is the delegate callback from BrowserSigin. +void ProfileSyncService::OnLoginSuccess() { + // The reason for the browser signin was a non-explicit passphrase + // required signal. If this is the first time through the passphrase + // process, we want to show the migration UI and offer an explicit + // passphrase. Otherwise, we're done because the implicit is enough. + + if (passphrase_required_for_decryption_) { + // NOT first time (decrypting something encrypted elsewhere). + // Do nothing. + } else { + ShowPassphraseMigration(NULL); + } +} + +// This is the delegate callback from BrowserSigin. +void ProfileSyncService::OnLoginFailure(const GoogleServiceAuthError& error) { + // Do nothing. The UI will already reflect the fact that the + // user is not signed in. +} + void ProfileSyncService::AddObserver(Observer* observer) { observers_.AddObserver(observer); } diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h index de66ec8..62a8e07 100644 --- a/chrome/browser/sync/profile_sync_service.h +++ b/chrome/browser/sync/profile_sync_service.h @@ -15,6 +15,7 @@ #include "base/string16.h" #include "base/time.h" #include "base/timer.h" +#include "chrome/browser/browser_signin.h" #include "chrome/browser/prefs/pref_member.h" #include "chrome/browser/sync/engine/syncapi.h" #include "chrome/browser/sync/glue/data_type_controller.h" @@ -37,6 +38,7 @@ class NotificationSource; class NotificationType; class Profile; class ProfileSyncFactory; +class TabContents; class TokenMigrator; // ProfileSyncService is the layer between browser subsystems like bookmarks, @@ -83,6 +85,7 @@ class TokenMigrator; // class ProfileSyncService : public browser_sync::SyncFrontend, public browser_sync::UnrecoverableErrorHandler, + public BrowserSignin::SigninDelegate, public NotificationObserver { public: typedef ProfileSyncServiceObserver Observer; @@ -223,6 +226,9 @@ class ProfileSyncService : public browser_sync::SyncFrontend, } virtual void ShowLoginDialog(gfx::NativeWindow parent_window); void ShowConfigure(gfx::NativeWindow parent_window); + void PromptForExistingPassphrase(gfx::NativeWindow parent_window); + void SigninForPassphrase(TabContents* container); + void ShowPassphraseMigration(gfx::NativeWindow parent_window); // Pretty-printed strings for a given StatusSummary. static std::string BuildSyncStatusSummaryText( @@ -319,6 +325,10 @@ class ProfileSyncService : public browser_sync::SyncFrontend, const NotificationSource& source, const NotificationDetails& details); + // BrowserSignin::SigninDelegate interface. + virtual void OnLoginSuccess(); + virtual void OnLoginFailure(const GoogleServiceAuthError& error); + // Changes which data types we're going to be syncing to |preferred_types|. // If it is running, the DataTypeManager will be instructed to reconfigure // the sync backend so that exactly these datatypes are actively synced. See diff --git a/chrome/browser/sync/resources/configure.html b/chrome/browser/sync/resources/configure.html index 90a3491..b087169 100644 --- a/chrome/browser/sync/resources/configure.html +++ b/chrome/browser/sync/resources/configure.html @@ -83,13 +83,11 @@ form { } #sync-encryption-instructions { - margin-bottom: 10px; - line-height: 1.8em; + margin-bottom: 5px; } #sync-passphrase-warning { - font-style: italic; - line-height: 1.8em; + margin-bottom: 5px; } #encryption-tab-contents > .sync_item_show { @@ -145,6 +143,26 @@ form { margin-right: 10px; margin-bottom: 10px; } +.sync-section { + background: #EEE; + margin: 5px 0px; + padding: 6px; +} + +#explicit-message { + margin-bottom: 5px; +} + +#change-passphrase { + margin: 10px 0; + background: #EEE; + padding: 8px; +} + +#clear-data-button { + margin-top: 10px; +} + html[dir='rtl'] .sync-footer { text-align: left; left: 0px; @@ -254,9 +272,9 @@ html[os='mac'] input[type='submit'] { args.syncApps; document.getElementById("appsItem").className = "sync-item-show"; } else { - document.getElementById("appsItem").className = "sync-item-hide";
- }
-
+ document.getElementById("appsItem").className = "sync-item-hide"; + } + setCheckboxesToKeepEverythingSynced(args.keepEverythingSynced); if (args.sessionsRegistered) { document.getElementById("sessionsCheckbox").checked = args.syncSessions; @@ -267,13 +285,18 @@ html[os='mac'] input[type='submit'] { } function setEncryptionCheckboxes(args) { - document.getElementById("usePassphraseCheckbox").checked = - args["usePassphrase"]; - - // The passphrase, once set, cannot be unset. if (args["usePassphrase"]) { - document.getElementById("usePassphraseCheckbox").disabled = true; + document.getElementById("explicit-option").checked = true; + + // The passphrase, once set, cannot be unset, but we show a reset link. + document.getElementById("explicit-option").disabled = true; + document.getElementById("google-option").disabled = true; + document.getElementById("change-passphrase").style.display = "block"; + } else { + document.getElementById("google-option").checked = true; + document.getElementById("change-passphrase").style.display = "none"; } + switchToMode(""); } function setErrorState(args) { @@ -298,6 +321,8 @@ html[os='mac'] input[type='submit'] { } } + + // Returns true if at least one data type is enabled and no data types are // checked. (If all data type checkboxes are disabled, it's because "keep // everything synced" is checked.) @@ -328,6 +353,10 @@ html[os='mac'] input[type='submit'] { } var f = document.getElementById("chooseDataTypesForm"); + if (!checkPassphraseMatch()) { + return false; + } + var syncAll = f.keepEverythingSyncedRadio.checked; // These values need to be kept in sync with where they are read in // SyncSetupFlow::GetDataTypeChoiceData(). @@ -342,7 +371,8 @@ html[os='mac'] input[type='submit'] { "syncTypedUrls": syncAll || f.typedUrlsCheckbox.checked, "syncApps": syncAll || f.appsCheckbox.checked, "syncSessions": syncAll || f.sessionsCheckbox.checked, - "usePassphrase": document.getElementById("usePassphraseCheckbox").checked + "usePassphrase": (getRadioCheckedValue() == 'explicit'), + "passphrase": f.passphrase.value }); chrome.send("Configure", [result]); } @@ -354,15 +384,73 @@ html[os='mac'] input[type='submit'] { document.getElementById(currentTab + "-tab-contents").className = "sync-config-tab-contents-inactive"; } - + document.getElementById(newTab + "-tab").className = "sync-config-tab-active"; document.getElementById(newTab + "-tab-contents").className = "sync-config-tab-contents-active"; - + currentTab = newTab; } + function switchToMode(mode) { + document.getElementById("section-explicit").style.display = "none"; + document.getElementById("section-google").style.display = "none"; + + if (mode == "google") { + document.getElementById("section-google").style.display = "block"; + } else if (mode =="explicit") { + document.getElementById("section-explicit").style.display = "block"; + } + } + + function getRadioCheckedValue() { + var f = document.getElementById("chooseDataTypesForm"); + for (var i = 0; i < f.option.length; ++i) { + if (f.option[i].checked) { + return f.option[i].value; + } + } + return undefined; + } + + function onRadioChange() { + switchToMode(getRadioCheckedValue()); + } + + function checkPassphraseMatch() { + var emptyError = document.getElementById("emptyerror"); + var mismatchError = document.getElementById("mismatcherror"); + emptyError.style.display = "none"; + mismatchError.style.display = "none"; + + if (getRadioCheckedValue() != "explicit") { + return true; + } + var f = document.getElementById("chooseDataTypesForm"); + if (f.passphrase.value.length == 0) { + emptyError.style.display = "block"; + return false; + } + if (f.confirmpassphrase.value.length > 0 && + f.confirmpassphrase.value != f.passphrase.value) { + mismatchError.style.display = "block"; + return false; + } + return true; + } + + function sendValuesAndClose() { + var result = JSON.stringify({"option": getRadioCheckedValue(), + "passphrase": f.passphrase.value}); + chrome.send("FirstPassphrase", [result]); + } + + function goToDashboard() { + chrome.send("GoToDashboard", [""]); + chrome.send("DialogClose", [""]); + } + </script> </head> <body i18n-values=".style.fontFamily:fontfamily" @@ -381,9 +469,9 @@ html[os='mac'] input[type='submit'] { </div> <div id="data-type-tab-contents" class="sync-config-tab-contents-inactive"> - <div class="sync-header" + <div class="sync-header" i18n-content="choosedatatypesheader"></div> - <div class="sync-choose_data_types_instructions" + <div class="sync-choose_data_types_instructions" i18n-content="choosedatatypesinstructions"></div> <div class="sync-select-customization"> <div class="sync-choice_radio"> @@ -395,7 +483,7 @@ html[os='mac'] input[type='submit'] { </label> </div> <div id="chooseDataTypes" class="sync-choice_radio"> - <input id="chooseDataTypesRadio" type="radio" name="syncChooseDataTypes" + <input id="chooseDataTypesRadio" type="radio" name="syncChooseDataTypes" onclick="setCheckboxesToKeepEverythingSynced(false)"> <label for="chooseDataTypesRadio" i18n-content="choosedatatypes" ></label> <div id="chooseDataTypesBody"> @@ -403,49 +491,49 @@ html[os='mac'] input[type='submit'] { <!-- Apps --> <div class="sync-item-show" id="appsItem"> <input id="appsCheckbox" name="dataTypeCheckbox" type="checkbox"> - <label id="appsCheckboxLabel" name="dataTypeLabel" + <label id="appsCheckboxLabel" name="dataTypeLabel" for="appsCheckbox" i18n-content="apps" i18n-values="title:apps"></label> </div> <!-- Autofill --> <div class="sync-item-show" id="autofillItem"> <input id="autofillCheckbox" name="dataTypeCheckbox" type="checkbox"> - <label id="autofillCheckboxLabel" name="dataTypeLabel" + <label id="autofillCheckboxLabel" name="dataTypeLabel" for="autofillCheckbox" i18n-content="autofill" i18n-values="title:autofill"></label> </div> <!-- Bookmarks --> <div class="sync-item-show" id="bookmarksItem"> <input id="bookmarksCheckbox" name="dataTypeCheckbox" type="checkbox"> - <label id="bookmarksCheckboxLabel" name="dataTypeLabel" + <label id="bookmarksCheckboxLabel" name="dataTypeLabel" for="bookmarksCheckbox" i18n-content="bookmarks" i18n-values="title:bookmarks"></label> </div> <!-- Extensions --> <div class="sync-item-show" id="extensionsItem"> <input id="extensionsCheckbox" name="dataTypeCheckbox" type="checkbox"> - <label id="extensionsCheckboxLabel" name="dataTypeLabel" + <label id="extensionsCheckboxLabel" name="dataTypeLabel" for="extensionsCheckbox" i18n-content="extensions" i18n-values="title:extensions"></label> </div> <!-- Omnibox --> <div class="sync-item-show" id="omniboxItem"> <input id="typedUrlsCheckbox" name="dataTypeCheckbox" type="checkbox"> - <label id="typedUrlsCheckboxLabel" name="dataTypeLabel" + <label id="typedUrlsCheckboxLabel" name="dataTypeLabel" for="typedUrlsCheckbox" i18n-content="typedurls" i18n-values="title:typedurls"></label> </div> <!-- Passwords --> <div class="sync-item-show" id="passwordsItem"> <input id="passwordsCheckbox" name="dataTypeCheckbox" type="checkbox"> - <label id="passwordsCheckboxLabel" name="dataTypeLabel" + <label id="passwordsCheckboxLabel" name="dataTypeLabel" for="passwordsCheckbox" i18n-content="passwords" i18n-values="title:passwords"></label> </div> <!-- Preferences --> <div class="sync-item-show" id="preferencesItem"> <input id="preferencesCheckbox" name="dataTypeCheckbox" type="checkbox"> - <label id="preferencesCheckboxLabel" name="dataTypeLabel" + <label id="preferencesCheckboxLabel" name="dataTypeLabel" for="preferencesCheckbox" i18n-content="preferences" i18n-values="title:preferences"></label> </div> @@ -458,7 +546,7 @@ html[os='mac'] input[type='submit'] { <!-- Sessions --> <div class="sync-item-show" id="sessionsItem"> <input id="sessionsCheckbox" name="dataTypeCheckbox" type="checkbox"> - <label id="sessionsCheckboxLabel" name="dataTypeLabel" + <label id="sessionsCheckboxLabel" name="dataTypeLabel" for="sessionsCheckbox" i18n-content="foreignsessions" il8n-values="title:sessions"></label> </div> @@ -475,21 +563,58 @@ html[os='mac'] input[type='submit'] { </div> <div id="encryption-tab-contents" class="sync-config-tab-contents-inactive"> - <div id="sync-encryption-instructions" + <div id="sync-encryption-instructions" i18n-content="encryptionInstructions"></div> - - <div class="sync-item-show" id="usePassphrase"> - <input id="usePassphraseCheckbox" name="usePassphraseCheckbox" - type="checkbox" /> - <label id="usePassphraseLabel" name="usePassphraseLabel" - for="usePassphraseCheckbox" i18n-content="usePassphraseLabel"> - </label> + + <div> + <input id="google-option" name="option" type="radio" + value="google" onchange="onRadioChange();"> + <span i18n-content="googleOption"></span> + </input> + </div> + <div> + <input id="explicit-option" name="option" type="radio" + value="explicit" onchange="onRadioChange();"> + <span i18n-content="explicitOption"></span> + </input> + </div> + + <div class="sync-section" id="section-google"> + <div i18n-content="sectionGoogleMessage"></div> + </div> + <div class="sync-section" id="section-explicit"> + <div i18n-content="sectionExplicitMessage" id="explicit-message"></div> + <div> + <div i18n-content="passphraseLabel" id="passphraseLabel"></div> + <input id="passphrase" name="passphrase" label="passphraseLabel" + type="password" onkeyup="checkPassphraseMatch();" + onchange="checkPassphraseMatch();"/> + </div> + <div> + <div i18n-content="confirmLabel" id="confirmPassphraseLabel"> + </div> + <input id="confirmpassphrase" name="confirmpassphrase" type="password" + label="confirmPassphraseLabel" + onkeyup="checkPassphraseMatch();" + onchange="checkPassphraseMatch();" /> + </div> + <div class="error" style="display:none" + id="emptyerror" i18n-content="emptyErrorMessage"></div> + <div class="error" style="display:none" + id="mismatcherror" i18n-content="mismatchErrorMessage"></div> </div> - - <div id="sync-passphrase-warning" i18n-content="passphraseWarning"> + + <div id="change-passphrase"> + <div id="sync-passphrase-warning" i18n-content="passphraseWarning"> + </div> + <div id="sync-reset-confirmation" i18n-content="cleardataconfirmation" + style="display: none;"> + </div> + <a id="clear-data-link" i18n-content="cleardatalink" href="#" + onclick='goToDashboard(); return false;'></a> </div> </div> - + <div class="sync-footer"> <input id="okButton" type="submit" i18n-values="value:ok" /> <input id="cancelButton" type="button" i18n-values="value:cancel" diff --git a/chrome/browser/sync/resources/encryption_login.html b/chrome/browser/sync/resources/encryption_login.html new file mode 100644 index 0000000..7883cca --- /dev/null +++ b/chrome/browser/sync/resources/encryption_login.html @@ -0,0 +1,6 @@ +<!-- This is intentionally an HTML fragment to be embedded + in the browser sign in HTML --> +<div id="login_message"> +<h1>$1</h1> +<p>$2</p> +</div> diff --git a/chrome/browser/sync/resources/firstpassphrase.html b/chrome/browser/sync/resources/firstpassphrase.html new file mode 100644 index 0000000..4f82ef9 --- /dev/null +++ b/chrome/browser/sync/resources/firstpassphrase.html @@ -0,0 +1,204 @@ +<html i18n-values="dir:textdirection;"> +<head> +<title></title> +<style type="text/css"> +body { + line-height: 1.5em; + background: #FFFFFF; + font-size: 11pt; +} +html[os='mac'] body { + line-height: 1.5em; + background: #FFFFFF; +} +form { + -webkit-user-select: none; +} +.error { + color: red; + font-size: 10pt; + } +.sync-header { + font-size: 1.2em; + font-weight: bold; + margin-bottom: 10px; +} +.sync-instructions { + margin-top: 10px; + margin-bottom: 10px; +} +.sync-footer { + position: fixed; + right: 0px; + bottom: 0px; + margin-right: 10px; + margin-bottom: 10px; +} +.sync-section { + background: #EEE; + margin: 5px; + padding: 10px; +} +html[dir='rtl'] .sync-footer { + text-align: left; + left: 0px; + bottom: 0px; + margin-left: 20px; +} +input[type='button'], +input[type='submit'] { + min-width: 87px; + min-height: 26px; +} +html[os='mac'] input[type='button'], +html[os='mac'] input[type='submit'] { + font-size: 12pt; +} + +#passphrase { + margin-top: 5px; +} + +#passphraseLabel, +#confirmPassphraseLabel { + width: 145px; + float: left; + text-align: right; + margin-right: 4px; + padding-top: 3px; +} + +</style> +<script src="chrome://resources/js/cr.js"></script> +<script> + var currentMode; + + // Called once, when this html/js is loaded. + function setupDialog(args) { + // Allow platform specific rules + if (cr.isMac) { + document.documentElement.setAttribute('os', 'mac'); + } else if (!cr.isWindows) { + document.documentElement.setAttribute('os', 'linux'); + } + + switchToMode(""); + } + + function switchToMode(mode) { + document.getElementById("section-explicit").style.display = "none"; + document.getElementById("section-nothanks").style.display = "none"; + document.getElementById("section-google").style.display = "none"; + + if (mode == "google") { + document.getElementById("section-google").style.display = "block"; + } else if (mode =="explicit") { + document.getElementById("section-explicit").style.display = "block"; + } else if (mode == "nothanks") { + document.getElementById("section-nothanks").style.display = "block"; + } + } + + function getRadioCheckedValue() { + var f = document.getElementById("form"); + for (var i = 0; i < f.option.length; ++i) { + if (f.option[i].checked) { + return f.option[i].value; + } + } + return undefined; + } + + function onRadioChange() { + switchToMode(getRadioCheckedValue()); + } + + function checkPassphraseMatch() { + var emptyError = document.getElementById("emptyerror"); + var mismatchError = document.getElementById("mismatcherror"); + emptyError.style.display = "none"; + mismatchError.style.display = "none"; + + if (getRadioCheckedValue() != "explicit") { + return true; + } + var f = document.getElementById("form"); + if (f.passphrase.value.length == 0) { + emptyError.style.display = "block"; + return false; + } + if (f.confirmpassphrase.value.length > 0 && + f.confirmpassphrase.value != f.passphrase.value) { + mismatchError.style.display = "block"; + return false; + } + return true; + } + + function sendValuesAndClose() { + var f = document.getElementById("form"); + if (!checkPassphraseMatch()) { + return false; + } + + var result = JSON.stringify({"option": getRadioCheckedValue(), + "passphrase": f.passphrase.value}); + chrome.send("FirstPassphrase", [result]); + } +</script> +</head> +<body i18n-values=".style.fontFamily:fontfamily" onload="setupDialog();"> +<form id="form" onSubmit="sendValuesAndClose(); return false;"> + <div class="sync-header" id="title" i18n-content="title"></div> + <div class="sync-instructions" id="instructions" + i18n-content="instructions"></div> + <div> + <input name="option" type="radio" value="google" onchange="onRadioChange();"> + <span i18n-content="googleOption"></span> + </input> + </div> + <div> + <input name="option" type="radio" value="explicit" onchange="onRadioChange();"> + <span i18n-content="explicitOption"></span> + </input> + </div> + <div> + <input name="option" type="radio" value="nothanks" onchange="onRadioChange();"> + <span i18n-content="nothanksOption"></span> + </input> + </div> + + <div class="sync-section" id="section-google"> + <div i18n-content="sectionGoogleMessage"></div> + </div> + <div class="sync-section" id="section-explicit"> + <div i18n-content="sectionExplicitMessage"></div> + <div> + <div i18n-content="passphraseLabel" id="passphraseLabel"></div> + <input id="passphrase" name="passphrase" label="passphraseLabel" + type="password" onkeyup="checkPassphraseMatch();" + onchange="checkPassphraseMatch();"/> + </div> + <div> + <div i18n-content="confirmLabel" id="confirmPassphraseLabel"> + </div> + <input id="confirmpassphrase" name="confirmpassphrase" type="password" + label="confirmPassphraseLabel" + onkeyup="checkPassphraseMatch();" + onchange="checkPassphraseMatch();" /> + </div> + <div class="error" style="display:none" + id="emptyerror" i18n-content="emptyErrorMessage"></div> + <div class="error" style="display:none" + id="mismatcherror" i18n-content="mismatchErrorMessage"></div> + </div> + <div class="sync-section" id="section-nothanks"> + <div i18n-content="sectionNothanksMessage"></div> + </div> + + <div class="sync-footer"> + <input id="okButton" type="submit" i18n-values="value:ok" /> + </div> +</form> +</body> +</html> diff --git a/chrome/browser/sync/resources/setup_flow.html b/chrome/browser/sync/resources/setup_flow.html index a3de363..495828f 100644 --- a/chrome/browser/sync/resources/setup_flow.html +++ b/chrome/browser/sync/resources/setup_flow.html @@ -3,7 +3,8 @@ <title></title> <script type="text/javascript"> function hideAllPages() { - var pages = ['login', 'configure', 'passphrase', 'settingup', 'done']; + var pages = ['login', 'configure', 'passphrase', + 'settingup', 'done', 'firstpassphrase']; for (var i = 0; i < pages.length; ++i) { document.getElementById(pages[i]).style.display = 'none'; document.getElementById(pages[i]).tabIndex = -1; @@ -21,6 +22,7 @@ function showPassphrase() { showPage('passphrase'); } function showSettingUp() { showPage('settingup'); } function showSetupDone() { showPage('done'); } + function showFirstPassphrase() { showPage('firstpassphrase'); } // Called once, when this html/js is loaded. function showTheRightIframe() { @@ -45,5 +47,8 @@ <iframe id="done" frameborder="0" width="100%" scrolling="no" height="100%" src="chrome://syncresources/setupdone" style="display:none" tabindex="-1"></iframe> + <iframe id="firstpassphrase" frameborder="0" width="100%" scrolling="no" + height="100%" src="chrome://syncresources/firstpassphrase" + style="display:none" tabindex="-1"></iframe> </body> </html> diff --git a/chrome/browser/sync/sync_setup_flow.cc b/chrome/browser/sync/sync_setup_flow.cc index 3d24877..adb06ea 100644 --- a/chrome/browser/sync/sync_setup_flow.cc +++ b/chrome/browser/sync/sync_setup_flow.cc @@ -44,6 +44,10 @@ void FlowHandler::RegisterMessages() { NewCallback(this, &FlowHandler::HandleConfigure)); dom_ui_->RegisterMessageCallback("Passphrase", NewCallback(this, &FlowHandler::HandlePassphraseEntry)); + dom_ui_->RegisterMessageCallback("FirstPassphrase", + NewCallback(this, &FlowHandler::HandleFirstPassphrase)); + dom_ui_->RegisterMessageCallback("GoToDashboard", + NewCallback(this, &FlowHandler::HandleGoToDashboard)); } static bool GetAuthData(const std::string& json, @@ -76,6 +80,18 @@ bool GetPassphrase(const std::string& json, std::string* passphrase, result->GetString("mode", mode); } +bool GetFirstPassphrase(const std::string& json, + std::string* option, + std::string* passphrase) { + scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false)); + if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY)) + return false; + + DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get()); + return result->GetString("option", option) && + result->GetString("passphrase", passphrase); +} + static bool GetConfiguration(const std::string& json, SyncConfiguration* config) { scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false)); @@ -145,6 +161,9 @@ static bool GetConfiguration(const std::string& json, // Encyption settings. if (!result->GetBoolean("usePassphrase", &config->use_secondary_passphrase)) return false; + if (config->use_secondary_passphrase && + !result->GetString("passphrase", &config->secondary_passphrase)) + return false; return true; } @@ -204,6 +223,28 @@ void FlowHandler::HandlePassphraseEntry(const ListValue* args) { flow_->OnPassphraseEntry(passphrase, mode); } +void FlowHandler::HandleFirstPassphrase(const ListValue* args) { + std::string json(dom_ui_util::GetJsonResponseFromFirstArgumentInList(args)); + + if (json.empty()) + return; + + std::string option; + std::string passphrase; + if (!GetFirstPassphrase(json, &option, &passphrase)) { + // Page sent result which couldn't be parsed. Programming error. + NOTREACHED(); + return; + } + + DCHECK(flow_); + flow_->OnFirstPassphraseEntry(option, passphrase); +} + +void FlowHandler::HandleGoToDashboard(const ListValue* args) { + flow_->OnGoToDashboard(); +} + // Called by SyncSetupFlow::Advance. void FlowHandler::ShowGaiaLogin(const DictionaryValue& args) { // Whenever you start a wizard, you pass in an arg so it starts on the right @@ -258,6 +299,17 @@ void FlowHandler::ShowPassphraseEntry(const DictionaryValue& args) { ExecuteJavascriptInIFrame(kPassphraseIFrameXPath, script); } +void FlowHandler::ShowFirstPassphrase(const DictionaryValue& args) { + if (dom_ui_) + dom_ui_->CallJavascriptFunction(L"showFirstPassphrase"); + + std::string json; + base::JSONWriter::Write(&args, false, &json); + std::wstring script = std::wstring(L"setupDialog") + + L"(" + UTF8ToWide(json) + L");"; + ExecuteJavascriptInIFrame(kPassphraseIFrameXPath, script); +} + void FlowHandler::ShowSettingUp() { if (dom_ui_) dom_ui_->CallJavascriptFunction(L"showSettingUp"); @@ -306,7 +358,6 @@ SyncSetupFlow::SyncSetupFlow(SyncSetupWizard::State start_state, login_start_time_(base::TimeTicks::Now()), flow_handler_(new FlowHandler()), owns_flow_handler_(true), - configuration_pending_(false), service_(service), html_dialog_window_(NULL) { flow_handler_->set_flow(this); @@ -503,8 +554,6 @@ bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) { return current_state_ == SyncSetupWizard::GAIA_LOGIN; case SyncSetupWizard::CONFIGURE: return current_state_ == SyncSetupWizard::GAIA_SUCCESS; - case SyncSetupWizard::CREATE_PASSPHRASE: - return current_state_ == SyncSetupWizard::CONFIGURE; case SyncSetupWizard::ENTER_PASSPHRASE: return current_state_ == SyncSetupWizard::CONFIGURE || current_state_ == SyncSetupWizard::SETTING_UP; @@ -512,8 +561,8 @@ bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) { return current_state_ == SyncSetupWizard::CONFIGURE; case SyncSetupWizard::SETTING_UP: return current_state_ == SyncSetupWizard::CONFIGURE || - current_state_ == SyncSetupWizard::CREATE_PASSPHRASE || - current_state_ == SyncSetupWizard::ENTER_PASSPHRASE; + current_state_ == SyncSetupWizard::ENTER_PASSPHRASE || + current_state_ == SyncSetupWizard::PASSPHRASE_MIGRATION; case SyncSetupWizard::FATAL_ERROR: return true; // You can always hit the panic button. case SyncSetupWizard::DONE_FIRST_TIME: @@ -554,16 +603,16 @@ void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) { flow_handler_->ShowConfigure(args); break; } - case SyncSetupWizard::CREATE_PASSPHRASE: { + case SyncSetupWizard::ENTER_PASSPHRASE: { DictionaryValue args; - args.SetString("mode", "new"); + SyncSetupFlow::GetArgsForEnterPassphrase(service_, &args); flow_handler_->ShowPassphraseEntry(args); break; } - case SyncSetupWizard::ENTER_PASSPHRASE: { + case SyncSetupWizard::PASSPHRASE_MIGRATION: { DictionaryValue args; - SyncSetupFlow::GetArgsForEnterPassphrase(service_, &args); - flow_handler_->ShowPassphraseEntry(args); + args.SetString("iframeToShow", "firstpassphrase"); + flow_handler_->ShowFirstPassphrase(args); break; } case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR: { @@ -630,6 +679,9 @@ SyncSetupFlow* SyncSetupFlow::Run(ProfileSyncService* service, SyncSetupFlow::GetArgsForConfigure(service, &args); else if (start == SyncSetupWizard::ENTER_PASSPHRASE) SyncSetupFlow::GetArgsForEnterPassphrase(service, &args); + else if (start == SyncSetupWizard::PASSPHRASE_MIGRATION) + args.SetString("iframeToShow", "firstpassphrase"); + std::string json_args; base::JSONWriter::Write(&args, false, &json_args); @@ -664,56 +716,47 @@ void SyncSetupFlow::OnUserSubmittedAuth(const std::string& username, } void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) { - // Store the configuration in case we need more information. - configuration_ = configuration; - configuration_pending_ = true; - - // If the user is activating secondary passphrase for the first time, - // we need to prompt them to enter one. - if (configuration.use_secondary_passphrase && - !service_->IsUsingSecondaryPassphrase()) { - // TODO(tim): If we could download the Nigori node first before any other - // types, we could do that prior to showing the configure page so that we - // could pre-populate the 'Use an encryption passphrase' checkbox. - // http://crbug.com/60182 - Advance(SyncSetupWizard::CREATE_PASSPHRASE); - return; - } - - OnConfigurationComplete(); -} - -void SyncSetupFlow::OnConfigurationComplete() { - if (!configuration_pending_) - return; - // Go to the "loading..." screen." Advance(SyncSetupWizard::SETTING_UP); // If we are activating the passphrase, we need to have one supplied. DCHECK(service_->IsUsingSecondaryPassphrase() || - !configuration_.use_secondary_passphrase || - configuration_.secondary_passphrase.length() > 0); + !configuration.use_secondary_passphrase || + configuration.secondary_passphrase.length() > 0); - if (configuration_.use_secondary_passphrase && + if (configuration.use_secondary_passphrase && !service_->IsUsingSecondaryPassphrase()) { - service_->SetPassphrase(configuration_.secondary_passphrase, true); + service_->SetPassphrase(configuration.secondary_passphrase, true); } - service_->OnUserChoseDatatypes(configuration_.sync_everything, - configuration_.data_types); - - configuration_pending_ = false; + service_->OnUserChoseDatatypes(configuration.sync_everything, + configuration.data_types); } void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase, const std::string& mode) { - if (current_state_ == SyncSetupWizard::ENTER_PASSPHRASE) { - service_->SetPassphrase(passphrase, mode == std::string("enter")); - Advance(SyncSetupWizard::SETTING_UP); - } else if (configuration_pending_) { - DCHECK_EQ(SyncSetupWizard::CREATE_PASSPHRASE, current_state_); - configuration_.secondary_passphrase = passphrase; - OnConfigurationComplete(); + Advance(SyncSetupWizard::SETTING_UP); + service_->SetPassphrase(passphrase, true); +} + +void SyncSetupFlow::OnFirstPassphraseEntry(const std::string& option, + const std::string& passphrase) { + Advance(SyncSetupWizard::SETTING_UP); + if (option == "explicit") { + service_->SetPassphrase(passphrase, true); + } else if (option == "nothanks") { + // User opted out of encrypted sync, need to turn off encrypted + // data types. + syncable::ModelTypeSet registered_types; + service_->GetRegisteredDataTypes(®istered_types); + registered_types.erase(syncable::PASSWORDS); + service_->OnUserChoseDatatypes(false, registered_types); + } else if (option == "google") { + // Implicit passphrase already set up. + Advance(SyncSetupWizard::DONE); } } + +void SyncSetupFlow::OnGoToDashboard() { + BrowserList::GetLastActive()->OpenPrivacyDashboardTabAndActivate(); +} diff --git a/chrome/browser/sync/sync_setup_flow.h b/chrome/browser/sync/sync_setup_flow.h index 8bfc443..f6c0233 100644 --- a/chrome/browser/sync/sync_setup_flow.h +++ b/chrome/browser/sync/sync_setup_flow.h @@ -110,7 +110,10 @@ class SyncSetupFlow : public HtmlDialogUIDelegate { void OnPassphraseEntry(const std::string& passphrase, const std::string& mode); - void OnConfigurationComplete(); + void OnFirstPassphraseEntry(const std::string& option, + const std::string& passphrase); + + void OnGoToDashboard(); private: FRIEND_TEST_ALL_PREFIXES(SyncSetupWizardTest, InitialStepLogin); @@ -124,6 +127,7 @@ class SyncSetupFlow : public HtmlDialogUIDelegate { FRIEND_TEST_ALL_PREFIXES(SyncSetupWizardTest, DiscreteRunChooseDataTypesAbortedByPendingClear); FRIEND_TEST_ALL_PREFIXES(SyncSetupWizardTest, EnterPassphraseRequired); + FRIEND_TEST_ALL_PREFIXES(SyncSetupWizardTest, PassphraseMigration); // Use static Run method to get an instance. SyncSetupFlow(SyncSetupWizard::State start_state, @@ -149,11 +153,6 @@ class SyncSetupFlow : public HtmlDialogUIDelegate { FlowHandler* flow_handler_; mutable bool owns_flow_handler_; - // The current configuration, held pending until all the information has - // been populated (possibly using multiple dialog states). - SyncConfiguration configuration_; - bool configuration_pending_; - // We need this to write the sentinel "setup completed" pref. ProfileSyncService* service_; @@ -198,6 +197,8 @@ class FlowHandler : public DOMMessageHandler { void HandleSubmitAuth(const ListValue* args); void HandleConfigure(const ListValue* args); void HandlePassphraseEntry(const ListValue* args); + void HandleFirstPassphrase(const ListValue* args); + void HandleGoToDashboard(const ListValue* args); // These functions control which part of the HTML is visible. void ShowGaiaLogin(const DictionaryValue& args); @@ -205,6 +206,7 @@ class FlowHandler : public DOMMessageHandler { void ShowGaiaSuccessAndSettingUp(); void ShowConfigure(const DictionaryValue& args); void ShowPassphraseEntry(const DictionaryValue& args); + void ShowFirstPassphrase(const DictionaryValue& args); void ShowSettingUp(); void ShowSetupDone(const std::wstring& user); void ShowFirstTimeDone(const std::wstring& user); diff --git a/chrome/browser/sync/sync_setup_wizard.cc b/chrome/browser/sync/sync_setup_wizard.cc index e80afe0..6e9d254 100644 --- a/chrome/browser/sync/sync_setup_wizard.cc +++ b/chrome/browser/sync/sync_setup_wizard.cc @@ -79,6 +79,7 @@ void SyncResourcesSource::StartDataRequest(const std::string& path_raw, const char kSyncGaiaLoginPath[] = "gaialogin"; const char kSyncConfigurePath[] = "configure"; const char kSyncPassphrasePath[] = "passphrase"; + const char kSyncFirstPassphrasePath[] = "firstpassphrase"; const char kSyncSettingUpPath[] = "settingup"; const char kSyncSetupDonePath[] = "setupdone"; @@ -149,23 +150,48 @@ void SyncResourcesSource::StartDataRequest(const std::string& path_raw, GetStringFUTF16(IDS_SYNC_ENCRYPTION_INSTRUCTIONS, GetStringUTF16(IDS_PRODUCT_NAME))); AddString(dict, "encryptAllLabel", IDS_SYNC_ENCRYPT_ALL_LABEL); - AddString(dict, "usePassphraseLabel", IDS_SYNC_PASSPHRASE_CHECKBOX_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); // 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, "newPassphraseTitle", IDS_SYNC_NEW_PASSPHRASE_TITLE); - AddString(dict, "newPassphraseBody", IDS_SYNC_NEW_PASSPHRASE_BODY); AddString(dict, "enterPassphraseTitle", IDS_SYNC_ENTER_PASSPHRASE_TITLE); AddString(dict, "enterPassphraseBody", IDS_SYNC_ENTER_PASSPHRASE_BODY); - AddString(dict, "gaiaPassphraseTitle", IDS_SYNC_GAIA_PASSPHRASE_TITLE); - AddString(dict, "gaiaPassphraseBody", IDS_SYNC_GAIA_PASSPHRASE_BODY); AddString(dict, "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL); 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, "nothanksOption", IDS_SYNC_PASSPHRASE_OPT_CANCEL); + AddString(dict, "sectionGoogleMessage", IDS_SYNC_PASSPHRASE_MSG_GOOGLE); + AddString(dict, "sectionExplicitMessage", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT); + AddString(dict, "sectionNothanksMessage", IDS_SYNC_PASSPHRASE_MSG_CANCEL); + 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, "ok", IDS_OK); + AddString(dict, "cancel", IDS_CANCEL); } else if (path_raw == kSyncSettingUpPath) { html_resource_id = IDR_SYNC_SETTING_UP_HTML; @@ -279,9 +305,9 @@ SyncSetupWizard::State SyncSetupWizard::GetEndStateForDiscreteRun( State result = FATAL_ERROR; if (start_state == GAIA_LOGIN) { result = GAIA_SUCCESS; - } else if (start_state == ENTER_PASSPHRASE) { - result = DONE; - } else if (start_state == CONFIGURE) { + } else if (start_state == ENTER_PASSPHRASE || + start_state == CONFIGURE || + start_state == PASSPHRASE_MIGRATION) { result = DONE; } DCHECK_NE(FATAL_ERROR, result) << diff --git a/chrome/browser/sync/sync_setup_wizard.h b/chrome/browser/sync/sync_setup_wizard.h index dad3fa9..76b6fae 100644 --- a/chrome/browser/sync/sync_setup_wizard.h +++ b/chrome/browser/sync/sync_setup_wizard.h @@ -29,10 +29,11 @@ class SyncSetupWizard { // Encryption -- // Choose what to encrypt and whether to use a passphrase. CONFIGURE, - // Show the screen that lets you enter a new passphrase - CREATE_PASSPHRASE, // Show the screen that prompts for your passphrase ENTER_PASSPHRASE, + // Show the passphrase "first time" screen for migrating users, where all + // is explained and they choose between google password/custom passphrase. + PASSPHRASE_MIGRATION, // The panic switch. Something went terribly wrong during setup and we // can't recover. FATAL_ERROR, diff --git a/chrome/browser/sync/sync_setup_wizard_unittest.cc b/chrome/browser/sync/sync_setup_wizard_unittest.cc index 46a0d69..b0da0d7b 100644 --- a/chrome/browser/sync/sync_setup_wizard_unittest.cc +++ b/chrome/browser/sync/sync_setup_wizard_unittest.cc @@ -312,6 +312,7 @@ TEST_F(SyncSetupWizardTest, InitialStepLogin) { TEST_F(SyncSetupWizardTest, ChooseDataTypesSetsPrefs) { SKIP_TEST_ON_MACOSX(); wizard_->Step(SyncSetupWizard::GAIA_LOGIN); + wizard_->Step(SyncSetupWizard::GAIA_SUCCESS); wizard_->Step(SyncSetupWizard::CONFIGURE); ListValue data_type_choices_value; @@ -359,6 +360,22 @@ TEST_F(SyncSetupWizardTest, EnterPassphraseRequired) { EXPECT_EQ("myPassphrase", service_->passphrase_); } +TEST_F(SyncSetupWizardTest, PassphraseMigration) { + SKIP_TEST_ON_MACOSX(); + wizard_->Step(SyncSetupWizard::PASSPHRASE_MIGRATION); + ListValue value; + value.Append(new StringValue("{\"option\":\"explicit\"," + "\"passphrase\":\"myPassphrase\"}")); + test_window_->flow()->flow_handler_->HandleFirstPassphrase(&value); + EXPECT_EQ("myPassphrase", service_->passphrase_); + + ListValue value2; + value2.Append(new StringValue("{\"option\":\"nothanks\"," + "\"passphrase\":\"myPassphrase\"}")); + test_window_->flow()->flow_handler_->HandleFirstPassphrase(&value2); + EXPECT_EQ(service_->chosen_data_types_.count(syncable::PASSWORDS), 0U); +} + TEST_F(SyncSetupWizardTest, DialogCancelled) { SKIP_TEST_ON_MACOSX(); wizard_->Step(SyncSetupWizard::GAIA_LOGIN); diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc index 43a97d0..d49ada1 100644 --- a/chrome/browser/sync/sync_ui_util.cc +++ b/chrome/browser/sync/sync_ui_util.cc @@ -5,6 +5,7 @@ #include "chrome/browser/sync/sync_ui_util.h" #include "app/l10n_util.h" +#include "app/resource_bundle.h" #include "base/i18n/number_formatting.h" #include "base/i18n/time_formatting.h" #include "base/string_util.h" @@ -13,6 +14,7 @@ #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/ui/options/options_window.h" #include "chrome/common/net/gaia/google_service_auth_error.h" +#include "grit/browser_resources.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" @@ -102,8 +104,28 @@ MessageType GetStatusInfo(ProfileSyncService* service, l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL)); } result_type = PRE_SYNCED; - } else if (auth_error.state() != AuthError::NONE || - service->observed_passphrase_required()) { + } else if (service->observed_passphrase_required()) { + if (service->passphrase_required_for_decryption()) { + // NOT first machine. + // Show a link and present as an error ("needs attention"). + if (status_label && link_label) { + status_label->assign(string16()); + link_label->assign( + l10n_util::GetStringUTF16(IDS_SYNC_CONFIGURE_ENCRYPTION)); + } + result_type = SYNC_ERROR; + } else { + // First machine. Show as a promotion. + if (status_label && link_label) { + status_label->assign( + l10n_util::GetStringFUTF16(IDS_SYNC_NTP_PASSWORD_PROMO, + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); + link_label->assign( + l10n_util::GetStringUTF16(IDS_SYNC_NTP_PASSWORD_ENABLE)); + } + result_type = SYNC_PROMO; + } + } else if (auth_error.state() != AuthError::NONE) { if (status_label && link_label) { GetStatusLabelsForAuthError(auth_error, service, status_label, link_label); @@ -156,6 +178,20 @@ MessageType GetStatusInfo(ProfileSyncService* service, } // namespace +// Returns an HTML chunk for a login prompt related to encryption. +string16 GetLoginMessageForEncryption() { + std::vector<std::string> subst; + const base::StringPiece html( + ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_SYNC_ENCRYPTION_LOGIN_HTML)); + subst.push_back(l10n_util::GetStringUTF8(IDS_SYNC_PLEASE_SIGN_IN)); + subst.push_back( + l10n_util::GetStringFUTF8(IDS_SYNC_LOGIN_FOR_ENCRYPTION, + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME))); + + return UTF8ToUTF16(ReplaceStringPlaceholders(html, subst, NULL)); +} + MessageType GetStatusLabels(ProfileSyncService* service, string16* status_label, string16* link_label) { diff --git a/chrome/browser/sync/sync_ui_util.h b/chrome/browser/sync/sync_ui_util.h index 8bcd85a..5a168c4 100644 --- a/chrome/browser/sync/sync_ui_util.h +++ b/chrome/browser/sync/sync_ui_util.h @@ -24,11 +24,15 @@ enum MessageType { PRE_SYNCED, // User has not set up sync. SYNCED, // We are synced and authenticated to a gmail account. SYNC_ERROR, // A sync error (such as invalid credentials) has occurred. + SYNC_PROMO, // A situation has occurred which should be brought to the user's + // attention, but not as an error. }; // TODO(akalin): audit the use of ProfileSyncService* service below, // and use const ProfileSyncService& service where possible. +string16 GetLoginMessageForEncryption(); + // Create status and link labels for the current status labels and link text // by querying |service|. MessageType GetStatusLabels(ProfileSyncService* service, @@ -60,4 +64,3 @@ void AddIntSyncDetail(ListValue* details, int64 stat_value); } // namespace sync_ui_util #endif // CHROME_BROWSER_SYNC_SYNC_UI_UTIL_H_ - |