• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
6 
7 #include "chrome/app/chrome_command_ids.h"
8 #include "chrome/browser/browsing_data/browsing_data_helper.h"
9 #include "chrome/browser/chrome_notification_types.h"
10 #include "chrome/browser/password_manager/password_store_factory.h"
11 #include "chrome/browser/ui/browser_command_controller.h"
12 #include "chrome/browser/ui/browser_finder.h"
13 #include "chrome/browser/ui/browser_window.h"
14 #include "chrome/browser/ui/chrome_pages.h"
15 #include "chrome/browser/ui/omnibox/location_bar.h"
16 #include "chrome/browser/ui/passwords/manage_passwords_icon.h"
17 #include "chrome/common/url_constants.h"
18 #include "components/password_manager/core/browser/password_store.h"
19 #include "content/public/browser/notification_service.h"
20 
21 using autofill::PasswordFormMap;
22 using password_manager::PasswordFormManager;
23 
24 namespace {
25 
GetPasswordStore(content::WebContents * web_contents)26 password_manager::PasswordStore* GetPasswordStore(
27     content::WebContents* web_contents) {
28   return PasswordStoreFactory::GetForProfile(
29              Profile::FromBrowserContext(web_contents->GetBrowserContext()),
30              Profile::EXPLICIT_ACCESS).get();
31 }
32 
33 } // namespace
34 
35 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ManagePasswordsUIController);
36 
ManagePasswordsUIController(content::WebContents * web_contents)37 ManagePasswordsUIController::ManagePasswordsUIController(
38     content::WebContents* web_contents)
39     : content::WebContentsObserver(web_contents),
40       state_(password_manager::ui::INACTIVE_STATE) {
41   password_manager::PasswordStore* password_store =
42       GetPasswordStore(web_contents);
43   if (password_store)
44     password_store->AddObserver(this);
45 }
46 
~ManagePasswordsUIController()47 ManagePasswordsUIController::~ManagePasswordsUIController() {}
48 
UpdateBubbleAndIconVisibility()49 void ManagePasswordsUIController::UpdateBubbleAndIconVisibility() {
50   // If we're not on a "webby" URL (e.g. "chrome://sign-in"), we shouldn't
51   // display either the bubble or the icon.
52   if (!BrowsingDataHelper::IsWebScheme(
53           web_contents()->GetLastCommittedURL().scheme())) {
54     state_ = password_manager::ui::INACTIVE_STATE;
55   }
56 
57   #if !defined(OS_ANDROID)
58     Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
59     if (!browser)
60       return;
61     LocationBar* location_bar = browser->window()->GetLocationBar();
62     DCHECK(location_bar);
63     location_bar->UpdateManagePasswordsIconAndBubble();
64   #endif
65 }
66 
OnPasswordSubmitted(PasswordFormManager * form_manager)67 void ManagePasswordsUIController::OnPasswordSubmitted(
68     PasswordFormManager* form_manager) {
69   form_manager_.reset(form_manager);
70   password_form_map_ = form_manager_->best_matches();
71   origin_ = PendingCredentials().origin;
72   state_ = password_manager::ui::PENDING_PASSWORD_AND_BUBBLE_STATE;
73   UpdateBubbleAndIconVisibility();
74 }
75 
OnPasswordAutofilled(const PasswordFormMap & password_form_map)76 void ManagePasswordsUIController::OnPasswordAutofilled(
77     const PasswordFormMap& password_form_map) {
78   password_form_map_ = password_form_map;
79   origin_ = password_form_map_.begin()->second->origin;
80   state_ = password_manager::ui::MANAGE_STATE;
81   UpdateBubbleAndIconVisibility();
82 }
83 
OnBlacklistBlockedAutofill(const PasswordFormMap & password_form_map)84 void ManagePasswordsUIController::OnBlacklistBlockedAutofill(
85     const PasswordFormMap& password_form_map) {
86   password_form_map_ = password_form_map;
87   origin_ = password_form_map_.begin()->second->origin;
88   state_ = password_manager::ui::BLACKLIST_STATE;
89   UpdateBubbleAndIconVisibility();
90 }
91 
WebContentsDestroyed()92 void ManagePasswordsUIController::WebContentsDestroyed() {
93   password_manager::PasswordStore* password_store =
94       GetPasswordStore(web_contents());
95   if (password_store)
96     password_store->RemoveObserver(this);
97 }
98 
OnLoginsChanged(const password_manager::PasswordStoreChangeList & changes)99 void ManagePasswordsUIController::OnLoginsChanged(
100     const password_manager::PasswordStoreChangeList& changes) {
101   password_manager::ui::State current_state = state_;
102   for (password_manager::PasswordStoreChangeList::const_iterator it =
103            changes.begin();
104        it != changes.end();
105        it++) {
106     const autofill::PasswordForm& changed_form = it->form();
107     if (changed_form.origin != origin_)
108       continue;
109 
110     if (it->type() == password_manager::PasswordStoreChange::REMOVE) {
111       password_form_map_.erase(changed_form.username_value);
112       if (changed_form.blacklisted_by_user)
113         state_ = password_manager::ui::MANAGE_STATE;
114     } else {
115       new_password_forms_.push_back(new autofill::PasswordForm(changed_form));
116       password_form_map_[changed_form.username_value] =
117           new_password_forms_.back();
118       if (changed_form.blacklisted_by_user)
119         state_ = password_manager::ui::BLACKLIST_STATE;
120     }
121   }
122   if (current_state != state_)
123     UpdateBubbleAndIconVisibility();
124 }
125 
126 void ManagePasswordsUIController::
NavigateToPasswordManagerSettingsPage()127     NavigateToPasswordManagerSettingsPage() {
128 // TODO(mkwst): chrome_pages.h is compiled out of Android. Need to figure out
129 // how this navigation should work there.
130 #if !defined(OS_ANDROID)
131   chrome::ShowSettingsSubPage(
132       chrome::FindBrowserWithWebContents(web_contents()),
133       chrome::kPasswordManagerSubPage);
134 #endif
135 }
136 
SavePassword()137 void ManagePasswordsUIController::SavePassword() {
138   DCHECK(PasswordPendingUserDecision());
139   DCHECK(form_manager_.get());
140   form_manager_->Save();
141   state_ = password_manager::ui::MANAGE_STATE;
142 }
143 
NeverSavePassword()144 void ManagePasswordsUIController::NeverSavePassword() {
145   DCHECK(PasswordPendingUserDecision());
146   DCHECK(form_manager_.get());
147   form_manager_->PermanentlyBlacklist();
148   state_ = password_manager::ui::BLACKLIST_STATE;
149   UpdateBubbleAndIconVisibility();
150 }
151 
UnblacklistSite()152 void ManagePasswordsUIController::UnblacklistSite() {
153   // We're in one of two states: either the user _just_ blacklisted the site
154   // by clicking "Never save" in the pending bubble, or the user is visiting
155   // a blacklisted site.
156   //
157   // Either way, |password_form_map_| has been populated with the relevant
158   // form. We can safely pull it out, send it over to the password store
159   // for removal, and update our internal state.
160   DCHECK(!password_form_map_.empty());
161   DCHECK(password_form_map_.begin()->second);
162   DCHECK(state_ == password_manager::ui::BLACKLIST_STATE);
163   password_manager::PasswordStore* password_store =
164       GetPasswordStore(web_contents());
165   if (password_store)
166     password_store->RemoveLogin(*password_form_map_.begin()->second);
167   state_ = password_manager::ui::MANAGE_STATE;
168   UpdateBubbleAndIconVisibility();
169 }
170 
DidNavigateMainFrame(const content::LoadCommittedDetails & details,const content::FrameNavigateParams & params)171 void ManagePasswordsUIController::DidNavigateMainFrame(
172     const content::LoadCommittedDetails& details,
173     const content::FrameNavigateParams& params) {
174   // Don't react to in-page (fragment) navigations.
175   if (details.is_in_page)
176     return;
177 
178   // Don't do anything if a navigation occurs before a user could reasonably
179   // interact with the password bubble.
180   if (timer_ && timer_->Elapsed() < base::TimeDelta::FromSeconds(1))
181     return;
182 
183   // Otherwise, reset the password manager and the timer.
184   state_ = password_manager::ui::INACTIVE_STATE;
185   UpdateBubbleAndIconVisibility();
186   timer_.reset(new base::ElapsedTimer());
187 }
188 
189 const autofill::PasswordForm& ManagePasswordsUIController::
PendingCredentials() const190     PendingCredentials() const {
191   DCHECK(form_manager_);
192   return form_manager_->pending_credentials();
193 }
194 
UpdateIconAndBubbleState(ManagePasswordsIcon * icon)195 void ManagePasswordsUIController::UpdateIconAndBubbleState(
196     ManagePasswordsIcon* icon) {
197   if (state_ == password_manager::ui::PENDING_PASSWORD_AND_BUBBLE_STATE) {
198     // We must display the icon before showing the bubble, as the bubble would
199     // be otherwise unanchored. However, we can't change the controller's state
200     // until _after_ the bubble is shown, as our metrics depend on the
201     // distinction between PENDING_PASSWORD_AND_BUBBLE_STATE and
202     // PENDING_PASSWORD_STATE to determine if the bubble opened automagically
203     // or via user action.
204     icon->SetState(password_manager::ui::PENDING_PASSWORD_STATE);
205     ShowBubbleWithoutUserInteraction();
206     state_ = password_manager::ui::PENDING_PASSWORD_STATE;
207   } else  {
208     icon->SetState(state_);
209   }
210 }
211 
ShowBubbleWithoutUserInteraction()212 void ManagePasswordsUIController::ShowBubbleWithoutUserInteraction() {
213   DCHECK_EQ(state_, password_manager::ui::PENDING_PASSWORD_AND_BUBBLE_STATE);
214 #if !defined(OS_ANDROID)
215   Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
216   if (!browser || browser->toolbar_model()->input_in_progress())
217     return;
218   CommandUpdater* updater = browser->command_controller()->command_updater();
219   updater->ExecuteCommand(IDC_MANAGE_PASSWORDS_FOR_PAGE);
220 #endif
221 }
222 
PasswordPendingUserDecision() const223 bool ManagePasswordsUIController::PasswordPendingUserDecision() const {
224   return state_ == password_manager::ui::PENDING_PASSWORD_STATE ||
225          state_ == password_manager::ui::PENDING_PASSWORD_AND_BUBBLE_STATE;
226 }
227