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