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