1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/webui/signin/inline_login_handler_impl.h"
7 #include "base/atomic_sequence_num.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/values.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/signin/signin_global_error.h"
15 #include "chrome/browser/signin/signin_oauth_helper.h"
16 #include "chrome/browser/signin/signin_promo.h"
17 #include "chrome/browser/sync/profile_sync_service.h"
18 #include "chrome/browser/sync/profile_sync_service_factory.h"
19 #include "chrome/browser/ui/browser_finder.h"
20 #include "chrome/browser/ui/sync/one_click_signin_helper.h"
21 #include "chrome/browser/ui/tabs/tab_strip_model.h"
22 #include "chrome/common/pref_names.h"
23 #include "content/public/browser/storage_partition.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/browser/web_ui.h"
26 #include "google_apis/gaia/gaia_auth_fetcher.h"
27 #include "google_apis/gaia/gaia_constants.h"
28 #include "google_apis/gaia/gaia_urls.h"
29 #include "net/base/url_util.h"
33 // Global SequenceNumber used for generating unique webview partition IDs.
34 base::StaticAtomicSequenceNumber next_partition_id
;
38 InlineLoginHandlerImpl::InlineLoginHandlerImpl()
39 : weak_factory_(this), choose_what_to_sync_(false), partition_id_("") {
42 InlineLoginHandlerImpl::~InlineLoginHandlerImpl() {}
44 void InlineLoginHandlerImpl::RegisterMessages() {
45 InlineLoginHandler::RegisterMessages();
47 web_ui()->RegisterMessageCallback("switchToFullTab",
48 base::Bind(&InlineLoginHandlerImpl::HandleSwitchToFullTabMessage
,
49 base::Unretained(this)));
52 void InlineLoginHandlerImpl::SetExtraInitParams(base::DictionaryValue
& params
) {
53 params
.SetInteger("authMode", InlineLoginHandler::kInlineAuthMode
);
55 const GURL
& current_url
= web_ui()->GetWebContents()->GetURL();
56 signin::Source source
= signin::GetSourceForPromoURL(current_url
);
57 DCHECK(source
!= signin::SOURCE_UNKNOWN
);
58 if (source
== signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT
||
59 source
== signin::SOURCE_AVATAR_BUBBLE_SIGN_IN
) {
60 // Drop the leading slash in the path.
61 params
.SetString("gaiaPath",
62 GaiaUrls::GetInstance()->embedded_signin_url().path().substr(1));
65 params
.SetString("service", "chromiumsync");
66 params
.SetString("continueUrl",
67 signin::GetLandingURL("source", static_cast<int>(source
)).spec());
69 if (source
!= signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT
) {
70 std::string last_email
= Profile::FromWebUI(web_ui())->GetPrefs()->
71 GetString(prefs::kGoogleServicesLastUsername
);
72 if (!last_email
.empty())
73 params
.SetString("email", last_email
);
76 std::string frame_url
;
77 net::GetValueForKeyInQuery(current_url
, "frameUrl", &frame_url
);
78 if (!frame_url
.empty())
79 params
.SetString("frameUrl", frame_url
);
81 std::string is_constrained
;
82 net::GetValueForKeyInQuery(current_url
, "constrained", &is_constrained
);
83 if (!is_constrained
.empty())
84 params
.SetString("constrained", is_constrained
);
86 net::GetValueForKeyInQuery(current_url
, "partitionId", &partition_id_
);
87 if (partition_id_
.empty()) {
89 "gaia-webview-" + base::IntToString(next_partition_id
.GetNext());
91 params
.SetString("partitionId", partition_id_
);
95 void InlineLoginHandlerImpl::HandleSwitchToFullTabMessage(
96 const base::ListValue
* args
) {
97 base::string16 url_str
;
98 CHECK(args
->GetString(0, &url_str
));
100 content::WebContents
* web_contents
= web_ui()->GetWebContents();
101 GURL
main_frame_url(web_contents
->GetURL());
102 main_frame_url
= net::AppendOrReplaceQueryParameter(
103 main_frame_url
, "frameUrl", UTF16ToASCII(url_str
));
104 main_frame_url
= net::AppendOrReplaceQueryParameter(
105 main_frame_url
, "partitionId", partition_id_
);
106 chrome::NavigateParams
params(
107 Profile::FromWebUI(web_ui()),
108 net::AppendOrReplaceQueryParameter(main_frame_url
, "constrained", "0"),
109 content::PAGE_TRANSITION_AUTO_TOPLEVEL
);
110 chrome::Navigate(¶ms
);
112 web_ui()->CallJavascriptFunction("inline.login.closeDialog");
115 void InlineLoginHandlerImpl::CompleteLogin(const base::ListValue
* args
) {
116 DCHECK(email_
.empty() && password_
.empty());
118 const base::DictionaryValue
* dict
= NULL
;
119 base::string16 email
;
120 if (!args
->GetDictionary(0, &dict
) || !dict
||
121 !dict
->GetString("email", &email
)) {
122 // User cancelled the signin by clicking 'skip for now'.
123 bool skip_for_now
= false;
124 DCHECK(dict
->GetBoolean("skipForNow", &skip_for_now
) && skip_for_now
);
126 signin::SetUserSkippedPromo(Profile::FromWebUI(web_ui()));
127 SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE
);
131 email_
= UTF16ToASCII(email
);
132 base::string16 password
;
133 dict
->GetString("password", &password
);
134 password_
= UTF16ToASCII(password
);
136 dict
->GetBoolean("chooseWhatToSync", &choose_what_to_sync_
);
138 content::WebContents
* contents
= web_ui()->GetWebContents();
139 signin::Source source
= signin::GetSourceForPromoURL(contents
->GetURL());
140 OneClickSigninHelper::CanOfferFor can_offer
=
141 source
== signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT
?
142 OneClickSigninHelper::CAN_OFFER_FOR_SECONDARY_ACCOUNT
:
143 OneClickSigninHelper::CAN_OFFER_FOR_ALL
;
144 std::string error_msg
;
145 OneClickSigninHelper::CanOffer(
146 contents
, can_offer
, email_
, &error_msg
);
147 if (!error_msg
.empty()) {
148 HandleLoginError(error_msg
);
152 content::StoragePartition
* partition
=
153 content::BrowserContext::GetStoragePartitionForSite(
154 contents
->GetBrowserContext(),
155 GURL("chrome-guest://mfffpogegjflfpflabcdkioaeobkgjik/?" +
158 auth_fetcher_
.reset(new GaiaAuthFetcher(this,
159 GaiaConstants::kChromeSource
,
160 partition
->GetURLRequestContext()));
161 auth_fetcher_
->StartCookieForOAuthCodeExchange("0");
164 void InlineLoginHandlerImpl::OnClientOAuthCodeSuccess(
165 const std::string
& oauth_code
) {
166 DCHECK(!oauth_code
.empty());
168 content::WebContents
* contents
= web_ui()->GetWebContents();
169 Profile
* profile
= Profile::FromWebUI(web_ui());
170 ProfileSyncService
* sync_service
=
171 ProfileSyncServiceFactory::GetForProfile(profile
);
172 const GURL
& current_url
= contents
->GetURL();
173 signin::Source source
= signin::GetSourceForPromoURL(current_url
);
175 if (source
== signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT
) {
176 // SigninOAuthHelper will delete itself.
177 SigninOAuthHelper
* helper
= new SigninOAuthHelper(profile
);
178 helper
->StartAddingAccount(oauth_code
);
180 OneClickSigninSyncStarter::StartSyncMode start_mode
=
181 source
== signin::SOURCE_SETTINGS
|| choose_what_to_sync_
?
182 (SigninGlobalError::GetForProfile(profile
)->HasMenuItem() &&
183 sync_service
&& sync_service
->HasSyncSetupCompleted()) ?
184 OneClickSigninSyncStarter::SHOW_SETTINGS_WITHOUT_CONFIGURE
:
185 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST
:
186 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS
;
187 OneClickSigninSyncStarter::ConfirmationRequired confirmation_required
=
188 source
== signin::SOURCE_SETTINGS
||
189 source
== signin::SOURCE_WEBSTORE_INSTALL
||
190 choose_what_to_sync_
?
191 OneClickSigninSyncStarter::NO_CONFIRMATION
:
192 OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN
;
193 OneClickSigninSyncStarter::Callback sync_callback
= base::Bind(
194 &InlineLoginHandlerImpl::SyncStarterCallback
,
195 weak_factory_
.GetWeakPtr());
197 bool cross_account_error_handled
=
198 OneClickSigninHelper::HandleCrossAccountError(
199 contents
, "" /* session_index, not used */,
200 email_
, password_
, oauth_code
,
201 OneClickSigninHelper::AUTO_ACCEPT_EXPLICIT
,
202 source
, start_mode
, sync_callback
);
204 if (!cross_account_error_handled
) {
205 // Call OneClickSigninSyncStarter to exchange oauth code for tokens.
206 // OneClickSigninSyncStarter will delete itself once the job is done.
207 new OneClickSigninSyncStarter(
208 profile
, NULL
, "" /* session_index, not used */,
209 email_
, password_
, oauth_code
,
212 confirmation_required
,
219 web_ui()->CallJavascriptFunction("inline.login.closeDialog");
222 void InlineLoginHandlerImpl::OnClientOAuthCodeFailure(
223 const GoogleServiceAuthError
& error
) {
224 LOG(ERROR
) << "InlineLoginUI::OnClientOAuthCodeFailure";
225 HandleLoginError(error
.ToString());
228 void InlineLoginHandlerImpl::HandleLoginError(const std::string
& error_msg
) {
229 SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE
);
231 Browser
* browser
= chrome::FindBrowserWithWebContents(
232 web_ui()->GetWebContents());
234 browser
= chrome::FindLastActiveWithProfile(
235 Profile::FromWebUI(web_ui()), chrome::GetActiveDesktop());
238 OneClickSigninHelper::ShowSigninErrorBubble(browser
, error_msg
);
244 void InlineLoginHandlerImpl::SyncStarterCallback(
245 OneClickSigninSyncStarter::SyncSetupResult result
) {
246 content::WebContents
* contents
= web_ui()->GetWebContents();
247 const GURL
& current_url
= contents
->GetURL();
248 bool auto_close
= signin::IsAutoCloseEnabledInURL(current_url
);
250 base::MessageLoop::current()->PostTask(
252 base::Bind(&InlineLoginHandlerImpl::CloseTab
,
253 weak_factory_
.GetWeakPtr()));
255 signin::Source source
= signin::GetSourceForPromoURL(current_url
);
256 DCHECK(source
!= signin::SOURCE_UNKNOWN
);
257 OneClickSigninHelper::RedirectToNtpOrAppsPageIfNecessary(contents
, source
);
261 void InlineLoginHandlerImpl::CloseTab() {
262 content::WebContents
* tab
= web_ui()->GetWebContents();
263 Browser
* browser
= chrome::FindBrowserWithWebContents(tab
);
265 TabStripModel
* tab_strip_model
= browser
->tab_strip_model();
266 if (tab_strip_model
) {
267 int index
= tab_strip_model
->GetIndexOfWebContents(tab
);
268 if (index
!= TabStripModel::kNoTab
) {
269 tab_strip_model
->ExecuteContextMenuCommand(
270 index
, TabStripModel::CommandCloseTab
);