1 // Copyright 2014 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/password_manager/chrome_password_manager_client.h"
8 #include "base/bind_helpers.h"
9 #include "base/command_line.h"
10 #include "base/memory/singleton.h"
11 #include "base/metrics/histogram.h"
12 #include "base/prefs/pref_service.h"
13 #include "chrome/browser/browsing_data/browsing_data_helper.h"
14 #include "chrome/browser/password_manager/password_store_factory.h"
15 #include "chrome/browser/password_manager/save_password_infobar_delegate.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/signin/signin_manager_factory.h"
18 #include "chrome/browser/sync/profile_sync_service.h"
19 #include "chrome/browser/sync/profile_sync_service_factory.h"
20 #include "chrome/browser/ui/autofill/password_generation_popup_controller_impl.h"
21 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
22 #include "chrome/common/channel_info.h"
23 #include "chrome/common/chrome_switches.h"
24 #include "chrome/common/url_constants.h"
25 #include "components/autofill/content/browser/content_autofill_driver.h"
26 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
27 #include "components/autofill/content/common/autofill_messages.h"
28 #include "components/autofill/core/browser/password_generator.h"
29 #include "components/autofill/core/common/password_form.h"
30 #include "components/password_manager/content/browser/content_password_manager_driver.h"
31 #include "components/password_manager/content/browser/password_manager_internals_service_factory.h"
32 #include "components/password_manager/content/common/credential_manager_messages.h"
33 #include "components/password_manager/core/browser/browser_save_password_progress_logger.h"
34 #include "components/password_manager/core/browser/log_receiver.h"
35 #include "components/password_manager/core/browser/password_form_manager.h"
36 #include "components/password_manager/core/browser/password_manager_internals_service.h"
37 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
38 #include "components/password_manager/core/browser/password_manager_util.h"
39 #include "components/password_manager/core/common/credential_manager_types.h"
40 #include "components/password_manager/core/common/password_manager_pref_names.h"
41 #include "components/password_manager/core/common/password_manager_switches.h"
42 #include "components/password_manager/sync/browser/password_sync_util.h"
43 #include "components/signin/core/browser/signin_manager.h"
44 #include "components/version_info/version_info.h"
45 #include "content/public/browser/navigation_entry.h"
46 #include "content/public/browser/render_view_host.h"
47 #include "content/public/browser/web_contents.h"
48 #include "google_apis/gaia/gaia_urls.h"
49 #include "net/base/url_util.h"
50 #include "third_party/re2/re2/re2.h"
52 #if defined(OS_ANDROID)
53 #include "chrome/browser/android/tab_android.h"
54 #include "chrome/browser/password_manager/generated_password_saved_infobar_delegate_android.h"
55 #include "chrome/browser/ui/android/snackbars/auto_signin_snackbar_controller.h"
58 using password_manager::ContentPasswordManagerDriverFactory
;
59 using password_manager::PasswordManagerInternalsService
;
60 using password_manager::PasswordManagerInternalsServiceFactory
;
62 // Shorten the name to spare line breaks. The code provides enough context
64 typedef autofill::SavePasswordProgressLogger Logger
;
66 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ChromePasswordManagerClient
);
70 const sync_driver::SyncService
* GetSyncService(Profile
* profile
) {
71 if (ProfileSyncServiceFactory::HasProfileSyncService(profile
))
72 return ProfileSyncServiceFactory::GetForProfile(profile
);
76 const SigninManagerBase
* GetSigninManager(Profile
* profile
) {
77 return SigninManagerFactory::GetForProfile(profile
);
80 // This routine is called when PasswordManagerClient is constructed.
81 // Currently we report metrics only once at startup. We require
82 // that this is only ever called from a single thread in order to
83 // avoid needing to lock (a static boolean flag is then sufficient to
84 // guarantee running only once).
85 void ReportMetrics(bool password_manager_enabled
,
86 password_manager::PasswordManagerClient
* client
,
88 static base::PlatformThreadId initial_thread_id
=
89 base::PlatformThread::CurrentId();
90 DCHECK_EQ(base::PlatformThread::CurrentId(), initial_thread_id
);
92 static bool ran_once
= false;
97 password_manager::PasswordStore
* store
= client
->GetPasswordStore();
98 // May be null in tests.
100 store
->ReportMetrics(
101 password_manager::sync_util::GetSyncUsernameIfSyncingPasswords(
102 GetSyncService(profile
), GetSigninManager(profile
)),
103 client
->GetPasswordSyncState() ==
104 password_manager::SYNCING_WITH_CUSTOM_PASSPHRASE
);
106 UMA_HISTOGRAM_BOOLEAN("PasswordManager.Enabled", password_manager_enabled
);
112 void ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
113 content::WebContents
* contents
,
114 autofill::AutofillClient
* autofill_client
) {
115 if (FromWebContents(contents
))
118 contents
->SetUserData(
120 new ChromePasswordManagerClient(contents
, autofill_client
));
123 ChromePasswordManagerClient::ChromePasswordManagerClient(
124 content::WebContents
* web_contents
,
125 autofill::AutofillClient
* autofill_client
)
126 : content::WebContentsObserver(web_contents
),
127 profile_(Profile::FromBrowserContext(web_contents
->GetBrowserContext())),
128 password_manager_(this),
129 driver_factory_(nullptr),
130 credential_manager_dispatcher_(web_contents
, this),
132 can_use_log_router_(false),
133 credentials_filter_(this,
134 base::Bind(&GetSyncService
, profile_
),
135 base::Bind(&GetSigninManager
, profile_
)) {
136 ContentPasswordManagerDriverFactory::CreateForWebContents(web_contents
, this,
139 ContentPasswordManagerDriverFactory::FromWebContents(web_contents
);
141 PasswordManagerInternalsService
* service
=
142 PasswordManagerInternalsServiceFactory::GetForBrowserContext(profile_
);
144 can_use_log_router_
= service
->RegisterClient(this);
145 saving_passwords_enabled_
.Init(
146 password_manager::prefs::kPasswordManagerSavingEnabled
, GetPrefs());
147 ReportMetrics(*saving_passwords_enabled_
, this, profile_
);
150 ChromePasswordManagerClient::~ChromePasswordManagerClient() {
151 PasswordManagerInternalsService
* service
=
152 PasswordManagerInternalsServiceFactory::GetForBrowserContext(profile_
);
154 service
->UnregisterClient(this);
157 bool ChromePasswordManagerClient::IsAutomaticPasswordSavingEnabled() const {
158 return base::CommandLine::ForCurrentProcess()->HasSwitch(
159 password_manager::switches::kEnableAutomaticPasswordSaving
) &&
160 chrome::GetChannel() == version_info::Channel::UNKNOWN
;
163 bool ChromePasswordManagerClient::IsPasswordManagementEnabledForCurrentPage()
165 DCHECK(web_contents());
166 content::NavigationEntry
* entry
=
167 web_contents()->GetController().GetLastCommittedEntry();
168 bool is_enabled
= false;
170 // TODO(gcasto): Determine if fix for crbug.com/388246 is relevant here.
172 } else if (IsURLPasswordWebsiteReauth(entry
->GetURL())) {
173 // Disable the password manager for online password management.
175 } else if (EnabledForSyncSignin()) {
178 // Do not fill nor save password when a user is signing in for sync. This
179 // is because users need to remember their password if they are syncing as
180 // this is effectively their master password.
181 is_enabled
= entry
->GetURL().host() != chrome::kChromeUIChromeSigninHost
;
183 if (IsLoggingActive()) {
184 password_manager::BrowserSavePasswordProgressLogger
logger(this);
186 Logger::STRING_PASSWORD_MANAGEMENT_ENABLED_FOR_CURRENT_PAGE
,
192 bool ChromePasswordManagerClient::IsSavingEnabledForCurrentPage() const {
193 return *saving_passwords_enabled_
&& !IsOffTheRecord() &&
194 !DidLastPageLoadEncounterSSLErrors() &&
195 IsPasswordManagementEnabledForCurrentPage();
198 bool ChromePasswordManagerClient::PromptUserToSaveOrUpdatePassword(
199 scoped_ptr
<password_manager::PasswordFormManager
> form_to_save
,
200 password_manager::CredentialSourceType type
,
201 bool update_password
) {
202 // Save password infobar and the password bubble prompts in case of
203 // "webby" URLs and do not prompt in case of "non-webby" URLS (e.g. file://).
204 if (!BrowsingDataHelper::IsWebScheme(
205 web_contents()->GetLastCommittedURL().scheme())) {
209 if (IsTheHotNewBubbleUIEnabled()) {
210 ManagePasswordsUIController
* manage_passwords_ui_controller
=
211 ManagePasswordsUIController::FromWebContents(web_contents());
212 if (update_password
&& IsUpdatePasswordUIEnabled()) {
213 manage_passwords_ui_controller
->OnUpdatePasswordSubmitted(
214 form_to_save
.Pass());
216 manage_passwords_ui_controller
->OnPasswordSubmitted(form_to_save
.Pass());
219 if (form_to_save
->IsBlacklisted())
221 std::string
uma_histogram_suffix(
222 password_manager::metrics_util::GroupIdToString(
223 password_manager::metrics_util::MonitoredDomainGroupId(
224 form_to_save
->pending_credentials().signon_realm
, GetPrefs())));
225 SavePasswordInfoBarDelegate::Create(
226 web_contents(), form_to_save
.Pass(), uma_histogram_suffix
, type
);
231 bool ChromePasswordManagerClient::PromptUserToChooseCredentials(
232 ScopedVector
<autofill::PasswordForm
> local_forms
,
233 ScopedVector
<autofill::PasswordForm
> federated_forms
,
235 base::Callback
<void(const password_manager::CredentialInfo
&)> callback
) {
236 return ManagePasswordsUIController::FromWebContents(web_contents())->
237 OnChooseCredentials(local_forms
.Pass(), federated_forms
.Pass(), origin
,
241 void ChromePasswordManagerClient::ForceSavePassword() {
242 password_manager::ContentPasswordManagerDriver
* driver
=
243 driver_factory_
->GetDriverForFrame(web_contents()->GetFocusedFrame());
244 driver
->ForceSavePassword();
247 void ChromePasswordManagerClient::NotifyUserAutoSignin(
248 ScopedVector
<autofill::PasswordForm
> local_forms
) {
249 DCHECK(!local_forms
.empty());
250 #if defined(OS_ANDROID)
251 TabAndroid
*tab
= TabAndroid::FromWebContents(web_contents());
252 ShowAutoSigninSnackbar(tab
, local_forms
[0]->username_value
);
254 ManagePasswordsUIController::FromWebContents(web_contents())->
255 OnAutoSignin(local_forms
.Pass());
260 void ChromePasswordManagerClient::AutomaticPasswordSave(
261 scoped_ptr
<password_manager::PasswordFormManager
> saved_form
) {
262 #if defined(OS_ANDROID)
263 GeneratedPasswordSavedInfoBarDelegateAndroid::Create(web_contents());
265 if (IsTheHotNewBubbleUIEnabled()) {
266 ManagePasswordsUIController
* manage_passwords_ui_controller
=
267 ManagePasswordsUIController::FromWebContents(web_contents());
268 manage_passwords_ui_controller
->OnAutomaticPasswordSave(
274 void ChromePasswordManagerClient::PasswordWasAutofilled(
275 const autofill::PasswordFormMap
& best_matches
) const {
276 ManagePasswordsUIController
* manage_passwords_ui_controller
=
277 ManagePasswordsUIController::FromWebContents(web_contents());
278 if (manage_passwords_ui_controller
&& IsTheHotNewBubbleUIEnabled())
279 manage_passwords_ui_controller
->OnPasswordAutofilled(best_matches
);
282 void ChromePasswordManagerClient::PasswordAutofillWasBlocked(
283 const autofill::PasswordFormMap
& best_matches
) const {
284 ManagePasswordsUIController
* controller
=
285 ManagePasswordsUIController::FromWebContents(web_contents());
286 if (controller
&& IsTheHotNewBubbleUIEnabled())
287 controller
->OnBlacklistBlockedAutofill(best_matches
);
290 void ChromePasswordManagerClient::HidePasswordGenerationPopup() {
291 if (popup_controller_
)
292 popup_controller_
->HideAndDestroy();
295 PrefService
* ChromePasswordManagerClient::GetPrefs() {
296 return profile_
->GetPrefs();
299 password_manager::PasswordStore
*
300 ChromePasswordManagerClient::GetPasswordStore() const {
301 // Always use EXPLICIT_ACCESS as the password manager checks IsOffTheRecord
302 // itself when it shouldn't access the PasswordStore.
303 // TODO(gcasto): Is is safe to change this to
304 // ServiceAccessType::IMPLICIT_ACCESS?
305 return PasswordStoreFactory::GetForProfile(
306 profile_
, ServiceAccessType::EXPLICIT_ACCESS
).get();
309 password_manager::PasswordSyncState
310 ChromePasswordManagerClient::GetPasswordSyncState() const {
311 const ProfileSyncService
* sync_service
=
312 ProfileSyncServiceFactory::GetForProfile(profile_
);
313 return password_manager_util::GetPasswordSyncState(sync_service
);
316 void ChromePasswordManagerClient::OnLogRouterAvailabilityChanged(
317 bool router_can_be_used
) {
318 if (can_use_log_router_
== router_can_be_used
)
320 can_use_log_router_
= router_can_be_used
;
322 NotifyRendererOfLoggingAvailability();
325 void ChromePasswordManagerClient::LogSavePasswordProgress(
326 const std::string
& text
) const {
327 if (!IsLoggingActive())
329 PasswordManagerInternalsService
* service
=
330 PasswordManagerInternalsServiceFactory::GetForBrowserContext(profile_
);
332 service
->ProcessLog(text
);
335 bool ChromePasswordManagerClient::IsLoggingActive() const {
336 // WebUI tabs do not need to log password saving progress. In particular, the
337 // internals page itself should not send any logs.
338 return can_use_log_router_
&& !web_contents()->GetWebUI();
341 bool ChromePasswordManagerClient::WasLastNavigationHTTPError() const {
342 DCHECK(web_contents());
344 scoped_ptr
<password_manager::BrowserSavePasswordProgressLogger
> logger
;
345 if (IsLoggingActive()) {
346 logger
.reset(new password_manager::BrowserSavePasswordProgressLogger(this));
348 Logger::STRING_WAS_LAST_NAVIGATION_HTTP_ERROR_METHOD
);
351 content::NavigationEntry
* entry
=
352 web_contents()->GetController().GetVisibleEntry();
355 int http_status_code
= entry
->GetHttpStatusCode();
358 logger
->LogNumber(Logger::STRING_HTTP_STATUS_CODE
, http_status_code
);
360 if (http_status_code
>= 400 && http_status_code
< 600)
365 bool ChromePasswordManagerClient::DidLastPageLoadEncounterSSLErrors() const {
366 content::NavigationEntry
* entry
=
367 web_contents()->GetController().GetLastCommittedEntry();
368 bool ssl_errors
= true;
372 ssl_errors
= net::IsCertStatusError(entry
->GetSSL().cert_status
);
374 if (IsLoggingActive()) {
375 password_manager::BrowserSavePasswordProgressLogger
logger(this);
376 logger
.LogBoolean(Logger::STRING_SSL_ERRORS_PRESENT
, ssl_errors
);
381 bool ChromePasswordManagerClient::IsOffTheRecord() const {
382 return web_contents()->GetBrowserContext()->IsOffTheRecord();
385 password_manager::PasswordManager
*
386 ChromePasswordManagerClient::GetPasswordManager() {
387 return &password_manager_
;
390 autofill::AutofillManager
*
391 ChromePasswordManagerClient::GetAutofillManagerForMainFrame() {
392 autofill::ContentAutofillDriverFactory
* factory
=
393 autofill::ContentAutofillDriverFactory::FromWebContents(web_contents());
395 ? factory
->DriverForFrame(web_contents()->GetMainFrame())
400 void ChromePasswordManagerClient::SetTestObserver(
401 autofill::PasswordGenerationPopupObserver
* observer
) {
402 observer_
= observer
;
405 bool ChromePasswordManagerClient::OnMessageReceived(
406 const IPC::Message
& message
,
407 content::RenderFrameHost
* render_frame_host
) {
409 IPC_BEGIN_MESSAGE_MAP_WITH_PARAM(ChromePasswordManagerClient
, message
,
411 // Autofill messages:
412 IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowPasswordGenerationPopup
,
413 ShowPasswordGenerationPopup
)
414 IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowPasswordEditingPopup
,
415 ShowPasswordEditingPopup
)
416 IPC_END_MESSAGE_MAP()
418 IPC_BEGIN_MESSAGE_MAP(ChromePasswordManagerClient
, message
)
419 IPC_MESSAGE_HANDLER(AutofillHostMsg_HidePasswordGenerationPopup
,
420 HidePasswordGenerationPopup
)
421 IPC_MESSAGE_HANDLER(AutofillHostMsg_GenerationAvailableForForm
,
422 GenerationAvailableForForm
)
423 IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordAutofillAgentConstructed
,
424 NotifyRendererOfLoggingAvailability
)
426 IPC_MESSAGE_UNHANDLED(handled
= false)
427 IPC_END_MESSAGE_MAP()
432 gfx::RectF
ChromePasswordManagerClient::GetBoundsInScreenSpace(
433 const gfx::RectF
& bounds
) {
434 gfx::Rect client_area
= web_contents()->GetContainerBounds();
435 return bounds
+ client_area
.OffsetFromOrigin();
438 void ChromePasswordManagerClient::ShowPasswordGenerationPopup(
439 content::RenderFrameHost
* render_frame_host
,
440 const gfx::RectF
& bounds
,
442 const autofill::PasswordForm
& form
) {
443 // TODO(gcasto): Validate data in PasswordForm.
445 gfx::RectF element_bounds_in_screen_space
= GetBoundsInScreenSpace(bounds
);
448 autofill::PasswordGenerationPopupControllerImpl::GetOrCreate(
449 popup_controller_
, element_bounds_in_screen_space
, form
, max_length
,
451 driver_factory_
->GetDriverForFrame(render_frame_host
), observer_
,
452 web_contents(), web_contents()->GetNativeView());
453 popup_controller_
->Show(true /* display_password */);
456 void ChromePasswordManagerClient::ShowPasswordEditingPopup(
457 content::RenderFrameHost
* render_frame_host
,
458 const gfx::RectF
& bounds
,
459 const autofill::PasswordForm
& form
) {
460 gfx::RectF element_bounds_in_screen_space
= GetBoundsInScreenSpace(bounds
);
462 autofill::PasswordGenerationPopupControllerImpl::GetOrCreate(
463 popup_controller_
, element_bounds_in_screen_space
, form
,
464 0, // Unspecified max length.
466 driver_factory_
->GetDriverForFrame(render_frame_host
), observer_
,
467 web_contents(), web_contents()->GetNativeView());
468 popup_controller_
->Show(false /* display_password */);
471 void ChromePasswordManagerClient::GenerationAvailableForForm(
472 const autofill::PasswordForm
& form
) {
473 password_manager_
.GenerationAvailableForForm(form
);
476 void ChromePasswordManagerClient::NotifyRendererOfLoggingAvailability() {
480 web_contents()->GetRenderViewHost()->Send(new AutofillMsg_SetLoggingState(
481 web_contents()->GetRenderViewHost()->GetRoutingID(),
482 can_use_log_router_
));
485 bool ChromePasswordManagerClient::IsURLPasswordWebsiteReauth(
486 const GURL
& url
) const {
487 if (url
.GetOrigin() != GaiaUrls::GetInstance()->gaia_url().GetOrigin())
490 // "rart" param signals this page is for transactional reauth.
491 std::string param_value
;
492 if (!net::GetValueForKeyInQuery(url
, "rart", ¶m_value
))
495 // Check the "continue" param to see if this reauth page is for the passwords
498 if (!net::GetValueForKeyInQuery(url
, "continue", ¶m_value
))
501 // All password sites, including test sites, have autofilling disabled.
502 CR_DEFINE_STATIC_LOCAL(RE2
, account_dashboard_pattern
,
503 ("passwords(-([a-z-]+\\.corp))?\\.google\\.com"));
505 return RE2::FullMatch(GURL(param_value
).host(), account_dashboard_pattern
);
508 bool ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled() {
509 #if !defined(USE_AURA) && !defined(OS_MACOSX)
512 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
513 if (command_line
->HasSwitch(switches::kDisableSavePasswordBubble
))
516 if (command_line
->HasSwitch(switches::kEnableSavePasswordBubble
))
519 std::string group_name
=
520 base::FieldTrialList::FindFullName("PasswordManagerUI");
522 // The bubble should be the default case that runs on the bots.
523 return group_name
!= "Infobar";
526 bool ChromePasswordManagerClient::IsUpdatePasswordUIEnabled() const {
527 #if defined(OS_MACOSX)
530 return IsTheHotNewBubbleUIEnabled();
534 bool ChromePasswordManagerClient::EnabledForSyncSignin() {
535 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
536 if (command_line
->HasSwitch(
537 password_manager::switches::kDisableManagerForSyncSignin
))
540 if (command_line
->HasSwitch(
541 password_manager::switches::kEnableManagerForSyncSignin
))
544 // Default is enabled.
545 std::string group_name
=
546 base::FieldTrialList::FindFullName("PasswordManagerStateForSyncSignin");
547 return group_name
!= "Disabled";
550 const GURL
& ChromePasswordManagerClient::GetMainFrameURL() const {
551 return web_contents()->GetVisibleURL();
554 const GURL
& ChromePasswordManagerClient::GetLastCommittedEntryURL() const {
555 DCHECK(web_contents());
556 content::NavigationEntry
* entry
=
557 web_contents()->GetController().GetLastCommittedEntry();
559 return GURL::EmptyGURL();
561 return entry
->GetURL();
564 const password_manager::CredentialsFilter
*
565 ChromePasswordManagerClient::GetStoreResultFilter() const {
566 return &credentials_filter_
;