OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/password_manager/password_form_manager.h" | 5 #include "chrome/browser/password_manager/password_form_manager.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/string_util.h" | 9 #include "base/string_util.h" |
10 #include "chrome/browser/password_manager/password_manager.h" | 10 #include "chrome/browser/password_manager/password_manager.h" |
11 #include "chrome/browser/profile.h" | 11 #include "chrome/browser/profile.h" |
12 #include "webkit/glue/password_form_dom_manager.h" | 12 #include "webkit/glue/password_form_dom_manager.h" |
13 | 13 |
14 using base::Time; | 14 using base::Time; |
15 | 15 |
16 PasswordFormManager::PasswordFormManager(Profile* profile, | 16 PasswordFormManager::PasswordFormManager(Profile* profile, |
17 PasswordManager* password_manager, | 17 PasswordManager* password_manager, |
18 const PasswordForm& observed_form, | 18 const PasswordForm& observed_form, |
19 bool ssl_valid) | 19 bool ssl_valid) |
20 : best_matches_deleter_(&best_matches_), | 20 : best_matches_deleter_(&best_matches_), |
21 observed_form_(observed_form), | 21 observed_form_(observed_form), |
22 is_new_login_(true), | 22 is_new_login_(true), |
23 password_manager_(password_manager), | 23 password_manager_(password_manager), |
24 pending_login_query_(NULL), | 24 pending_login_query_(0), |
25 preferred_match_(NULL), | 25 preferred_match_(NULL), |
26 state_(PRE_MATCHING_PHASE), | 26 state_(PRE_MATCHING_PHASE), |
27 profile_(profile) { | 27 profile_(profile) { |
28 DCHECK(profile_); | 28 DCHECK(profile_); |
29 if (observed_form_.origin.is_valid()) | 29 if (observed_form_.origin.is_valid()) |
30 SplitString(observed_form_.origin.path(), '/', &form_path_tokens_); | 30 SplitString(observed_form_.origin.path(), '/', &form_path_tokens_); |
31 observed_form_.ssl_valid = ssl_valid; | 31 observed_form_.ssl_valid = ssl_valid; |
32 } | 32 } |
33 | 33 |
34 PasswordFormManager::~PasswordFormManager() { | 34 PasswordFormManager::~PasswordFormManager() { |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
161 if (IsNewLogin()) | 161 if (IsNewLogin()) |
162 SaveAsNewLogin(); | 162 SaveAsNewLogin(); |
163 else | 163 else |
164 UpdateLogin(); | 164 UpdateLogin(); |
165 } | 165 } |
166 | 166 |
167 void PasswordFormManager::FetchMatchingLoginsFromWebDatabase() { | 167 void PasswordFormManager::FetchMatchingLoginsFromWebDatabase() { |
168 DCHECK_EQ(state_, PRE_MATCHING_PHASE); | 168 DCHECK_EQ(state_, PRE_MATCHING_PHASE); |
169 DCHECK(!pending_login_query_); | 169 DCHECK(!pending_login_query_); |
170 state_ = MATCHING_PHASE; | 170 state_ = MATCHING_PHASE; |
171 WebDataService* web_data_service = | 171 PasswordStore* password_store = |
172 profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); | 172 profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS); |
173 if (!web_data_service) { | 173 if (!password_store) { |
174 NOTREACHED(); | 174 NOTREACHED(); |
175 return; | 175 return; |
176 } | 176 } |
177 pending_login_query_ = web_data_service->GetLogins(observed_form_, this); | 177 pending_login_query_ = password_store->GetLogins(observed_form_, this); |
178 } | 178 } |
179 | 179 |
180 bool PasswordFormManager::HasCompletedMatching() { | 180 bool PasswordFormManager::HasCompletedMatching() { |
181 return state_ == POST_MATCHING_PHASE; | 181 return state_ == POST_MATCHING_PHASE; |
182 } | 182 } |
183 | 183 |
184 void PasswordFormManager::OnRequestDone(WebDataService::Handle h, | 184 void PasswordFormManager::OnRequestDone(int handle, |
185 const WDTypedResult* result) { | 185 const std::vector<PasswordForm*>& logins_result) { |
186 // Get the result from the database into a usable form. | |
187 const WDResult<std::vector<PasswordForm*> >* r = | |
188 static_cast<const WDResult<std::vector<PasswordForm*> >*>(result); | |
189 std::vector<PasswordForm*> logins_result = r->GetValue(); | |
190 // Note that the result gets deleted after this call completes, but we own | 186 // Note that the result gets deleted after this call completes, but we own |
191 // the PasswordForm objects pointed to by the result vector, thus we keep | 187 // the PasswordForm objects pointed to by the result vector, thus we keep |
192 // copies to a minimum here. | 188 // copies to a minimum here. |
193 | 189 |
194 int best_score = 0; | 190 int best_score = 0; |
195 std::vector<PasswordForm> empties; // Empty-path matches in result set. | 191 std::vector<PasswordForm> empties; // Empty-path matches in result set. |
196 for (size_t i = 0; i < logins_result.size(); i++) { | 192 for (size_t i = 0; i < logins_result.size(); i++) { |
197 if (IgnoreResult(*logins_result[i])) { | 193 if (IgnoreResult(*logins_result[i])) { |
198 delete logins_result[i]; | 194 delete logins_result[i]; |
199 continue; | 195 continue; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
232 best_matches_.clear(); | 228 best_matches_.clear(); |
233 preferred_match_ = NULL; // Don't delete, its owned by best_matches_. | 229 preferred_match_ = NULL; // Don't delete, its owned by best_matches_. |
234 best_matches_[logins_result[i]->username_value] = logins_result[i]; | 230 best_matches_[logins_result[i]->username_value] = logins_result[i]; |
235 } | 231 } |
236 preferred_match_ = logins_result[i]->preferred ? logins_result[i] | 232 preferred_match_ = logins_result[i]->preferred ? logins_result[i] |
237 : preferred_match_; | 233 : preferred_match_; |
238 } | 234 } |
239 // We're done matching now. | 235 // We're done matching now. |
240 state_ = POST_MATCHING_PHASE; | 236 state_ = POST_MATCHING_PHASE; |
241 | 237 |
242 if (best_score <= 0) { | |
243 #if defined(OS_WIN) | |
244 state_ = PRE_MATCHING_PHASE; | |
245 FetchMatchingIE7LoginFromWebDatabase(); | |
246 #endif | |
247 return; | |
248 } | |
249 | |
250 for (std::vector<PasswordForm>::const_iterator it = empties.begin(); | 238 for (std::vector<PasswordForm>::const_iterator it = empties.begin(); |
251 it != empties.end(); ++it) { | 239 it != empties.end(); ++it) { |
252 // If we don't already have a result with the same username, add the | 240 // If we don't already have a result with the same username, add the |
253 // lower-scored empty-path match (if it had equal score it would already be | 241 // lower-scored empty-path match (if it had equal score it would already be |
254 // in best_matches_). | 242 // in best_matches_). |
255 if (best_matches_.find(it->username_value) == best_matches_.end()) | 243 if (best_matches_.find(it->username_value) == best_matches_.end()) |
256 best_matches_[it->username_value] = new PasswordForm(*it); | 244 best_matches_[it->username_value] = new PasswordForm(*it); |
257 } | 245 } |
258 | 246 |
259 // Its possible we have at least one match but have no preferred_match_, | 247 // Its possible we have at least one match but have no preferred_match_, |
260 // because a user may have chosen to 'Forget' the preferred match. So we | 248 // because a user may have chosen to 'Forget' the preferred match. So we |
261 // just pick the first one and whichever the user selects for submit will | 249 // just pick the first one and whichever the user selects for submit will |
262 // be saved as preferred. | 250 // be saved as preferred. |
263 DCHECK(!best_matches_.empty()); | 251 DCHECK(!best_matches_.empty()); |
264 if (!preferred_match_) | 252 if (!preferred_match_) |
265 preferred_match_ = best_matches_.begin()->second; | 253 preferred_match_ = best_matches_.begin()->second; |
266 | 254 |
267 // Now we determine if the user told us to ignore this site in the past. | 255 // Now we determine if the user told us to ignore this site in the past. |
268 // If they haven't, we proceed to auto-fill. | 256 // If they haven't, we proceed to auto-fill. |
269 if (!preferred_match_->blacklisted_by_user) { | 257 if (!preferred_match_->blacklisted_by_user) { |
270 password_manager_->Autofill(observed_form_, best_matches_, | 258 password_manager_->Autofill(observed_form_, best_matches_, |
271 preferred_match_); | 259 preferred_match_); |
272 } | 260 } |
273 } | 261 } |
274 | 262 |
275 void PasswordFormManager::OnWebDataServiceRequestDone(WebDataService::Handle h, | 263 void PasswordFormManager::OnPasswordStoreRequestDone( |
276 const WDTypedResult* result) { | 264 int handle, const std::vector<PasswordForm*>& result) { |
277 DCHECK_EQ(state_, MATCHING_PHASE); | 265 DCHECK_EQ(state_, MATCHING_PHASE); |
278 DCHECK_EQ(pending_login_query_, h); | 266 DCHECK_EQ(pending_login_query_, handle); |
279 DCHECK(result); | |
280 pending_login_query_ = NULL; | |
281 | 267 |
282 if (!result) | 268 if (result.empty()) { |
| 269 state_ = POST_MATCHING_PHASE; |
283 return; | 270 return; |
| 271 } |
284 | 272 |
285 switch (result->GetType()) { | 273 OnRequestDone(handle, result); |
286 case PASSWORD_RESULT: { | |
287 OnRequestDone(h, result); | |
288 break; | |
289 } | |
290 #if defined(OS_WIN) | |
291 case PASSWORD_IE7_RESULT: { | |
292 OnIE7RequestDone(h, result); | |
293 break; | |
294 } | |
295 #endif | |
296 default: | |
297 NOTREACHED(); | |
298 } | |
299 } | 274 } |
300 | 275 |
301 bool PasswordFormManager::IgnoreResult(const PasswordForm& form) const { | 276 bool PasswordFormManager::IgnoreResult(const PasswordForm& form) const { |
302 // Ignore change password forms until we have some change password | 277 // Ignore change password forms until we have some change password |
303 // functionality | 278 // functionality |
304 if (observed_form_.old_password_element.length() != 0) { | 279 if (observed_form_.old_password_element.length() != 0) { |
305 return true; | 280 return true; |
306 } | 281 } |
307 // Don't match an invalid SSL form with one saved under secure | 282 // Don't match an invalid SSL form with one saved under secure |
308 // circumstances. | 283 // circumstances. |
309 if (form.ssl_valid && !observed_form_.ssl_valid) { | 284 if (form.ssl_valid && !observed_form_.ssl_valid) { |
310 return true; | 285 return true; |
311 } | 286 } |
312 return false; | 287 return false; |
313 } | 288 } |
314 | 289 |
315 void PasswordFormManager::SaveAsNewLogin() { | 290 void PasswordFormManager::SaveAsNewLogin() { |
316 DCHECK_EQ(state_, POST_MATCHING_PHASE); | 291 DCHECK_EQ(state_, POST_MATCHING_PHASE); |
317 DCHECK(IsNewLogin()); | 292 DCHECK(IsNewLogin()); |
318 // The new_form is being used to sign in, so it is preferred. | 293 // The new_form is being used to sign in, so it is preferred. |
319 DCHECK(pending_credentials_.preferred); | 294 DCHECK(pending_credentials_.preferred); |
320 // new_form contains the same basic data as observed_form_ (because its the | 295 // new_form contains the same basic data as observed_form_ (because its the |
321 // same form), but with the newly added credentials. | 296 // same form), but with the newly added credentials. |
322 | 297 |
323 DCHECK(!profile_->IsOffTheRecord()); | 298 DCHECK(!profile_->IsOffTheRecord()); |
324 | 299 |
325 WebDataService* web_data_service = | 300 PasswordStore* password_store = |
326 profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); | 301 profile_->GetPasswordStore(Profile::IMPLICIT_ACCESS); |
327 if (!web_data_service) { | 302 if (!password_store) { |
328 NOTREACHED(); | 303 NOTREACHED(); |
329 return; | 304 return; |
330 } | 305 } |
| 306 |
331 pending_credentials_.date_created = Time::Now(); | 307 pending_credentials_.date_created = Time::Now(); |
332 web_data_service->AddLogin(pending_credentials_); | 308 password_store->AddLogin(pending_credentials_); |
333 } | 309 } |
334 | 310 |
335 void PasswordFormManager::UpdateLogin() { | 311 void PasswordFormManager::UpdateLogin() { |
336 DCHECK_EQ(state_, POST_MATCHING_PHASE); | 312 DCHECK_EQ(state_, POST_MATCHING_PHASE); |
337 DCHECK(preferred_match_); | 313 DCHECK(preferred_match_); |
338 // If we're doing an Update, its because we autofilled a form and the user | 314 // If we're doing an Update, its because we autofilled a form and the user |
339 // submitted it with a possibly new password value, page security, or selected | 315 // submitted it with a possibly new password value, page security, or selected |
340 // one of the non-preferred matches, thus requiring a swap of preferred bits. | 316 // one of the non-preferred matches, thus requiring a swap of preferred bits. |
341 DCHECK(!IsNewLogin() && pending_credentials_.preferred); | 317 DCHECK(!IsNewLogin() && pending_credentials_.preferred); |
342 DCHECK(!profile_->IsOffTheRecord()); | 318 DCHECK(!profile_->IsOffTheRecord()); |
343 | 319 |
344 WebDataService* web_data_service = | 320 PasswordStore* password_store = |
345 profile_->GetWebDataService(Profile::IMPLICIT_ACCESS); | 321 profile_->GetPasswordStore(Profile::IMPLICIT_ACCESS); |
346 if (!web_data_service) { | 322 if (!password_store) { |
347 NOTREACHED(); | 323 NOTREACHED(); |
348 return; | 324 return; |
349 } | 325 } |
350 | 326 |
351 // Update all matches to reflect new preferred status. | 327 // Update all matches to reflect new preferred status. |
352 PasswordFormMap::iterator iter; | 328 PasswordFormMap::iterator iter; |
353 for (iter = best_matches_.begin(); iter != best_matches_.end(); iter++) { | 329 for (iter = best_matches_.begin(); iter != best_matches_.end(); iter++) { |
354 if ((iter->second->username_value != pending_credentials_.username_value) && | 330 if ((iter->second->username_value != pending_credentials_.username_value) && |
355 iter->second->preferred) { | 331 iter->second->preferred) { |
356 // This wasn't the selected login but it used to be preferred. | 332 // This wasn't the selected login but it used to be preferred. |
357 iter->second->preferred = false; | 333 iter->second->preferred = false; |
358 web_data_service->UpdateLogin(*iter->second); | 334 password_store->UpdateLogin(*iter->second); |
359 } | 335 } |
360 } | 336 } |
361 // Update the new preferred login. | 337 // Update the new preferred login. |
362 // Note origin.spec().length > signon_realm.length implies the origin has a | 338 // Note origin.spec().length > signon_realm.length implies the origin has a |
363 // path, since signon_realm is a prefix of origin for HTML password forms. | 339 // path, since signon_realm is a prefix of origin for HTML password forms. |
364 if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) && | 340 if ((observed_form_.scheme == PasswordForm::SCHEME_HTML) && |
365 (observed_form_.origin.spec().length() > | 341 (observed_form_.origin.spec().length() > |
366 observed_form_.signon_realm.length()) && | 342 observed_form_.signon_realm.length()) && |
367 (observed_form_.signon_realm == pending_credentials_.origin.spec())) { | 343 (observed_form_.signon_realm == pending_credentials_.origin.spec())) { |
368 // The user logged in successfully with one of our autofilled logins on a | 344 // The user logged in successfully with one of our autofilled logins on a |
369 // page with non-empty path, but the autofilled entry was initially saved/ | 345 // page with non-empty path, but the autofilled entry was initially saved/ |
370 // imported with an empty path. Rather than just mark this entry preferred, | 346 // imported with an empty path. Rather than just mark this entry preferred, |
371 // we create a more specific copy for this exact page and leave the "master" | 347 // we create a more specific copy for this exact page and leave the "master" |
372 // unchanged. This is to prevent the case where that master login is used | 348 // unchanged. This is to prevent the case where that master login is used |
373 // on several sites (e.g site.com/a and site.com/b) but the user actually | 349 // on several sites (e.g site.com/a and site.com/b) but the user actually |
374 // has a different preference on each site. For example, on /a, he wants the | 350 // has a different preference on each site. For example, on /a, he wants the |
375 // general empty-path login so it is flagged as preferred, but on /b he logs | 351 // general empty-path login so it is flagged as preferred, but on /b he logs |
376 // in with a different saved entry - we don't want to remove the preferred | 352 // in with a different saved entry - we don't want to remove the preferred |
377 // status of the former because upon return to /a it won't be the default- | 353 // status of the former because upon return to /a it won't be the default- |
378 // fill match. | 354 // fill match. |
379 // TODO(timsteele): Bug 1188626 - expire the master copies. | 355 // TODO(timsteele): Bug 1188626 - expire the master copies. |
380 PasswordForm copy(pending_credentials_); | 356 PasswordForm copy(pending_credentials_); |
381 copy.origin = observed_form_.origin; | 357 copy.origin = observed_form_.origin; |
382 copy.action = observed_form_.action; | 358 copy.action = observed_form_.action; |
383 web_data_service->AddLogin(copy); | 359 password_store->AddLogin(copy); |
384 } else { | 360 } else { |
385 web_data_service->UpdateLogin(pending_credentials_); | 361 password_store->UpdateLogin(pending_credentials_); |
386 } | 362 } |
387 } | 363 } |
388 | 364 |
389 void PasswordFormManager::CancelLoginsQuery() { | 365 void PasswordFormManager::CancelLoginsQuery() { |
390 if (!pending_login_query_) | 366 PasswordStore* password_store = |
391 return; | 367 profile_->GetPasswordStore(Profile::IMPLICIT_ACCESS); |
392 WebDataService* web_data_service = | 368 if (!password_store) { |
393 profile_->GetWebDataService(Profile::EXPLICIT_ACCESS); | 369 // Can be NULL in unit tests. |
394 if (!web_data_service) { | |
395 NOTREACHED(); | |
396 return; | 370 return; |
397 } | 371 } |
398 web_data_service->CancelRequest(pending_login_query_); | 372 password_store->CancelLoginsQuery(pending_login_query_); |
399 pending_login_query_ = NULL; | |
400 } | 373 } |
401 | 374 |
402 int PasswordFormManager::ScoreResult(const PasswordForm& candidate) const { | 375 int PasswordFormManager::ScoreResult(const PasswordForm& candidate) const { |
403 DCHECK_EQ(state_, MATCHING_PHASE); | 376 DCHECK_EQ(state_, MATCHING_PHASE); |
404 // For scoring of candidate login data: | 377 // For scoring of candidate login data: |
405 // The most important element that should match is the origin, followed by | 378 // The most important element that should match is the origin, followed by |
406 // the action, the password name, the submit button name, and finally the | 379 // the action, the password name, the submit button name, and finally the |
407 // username input field name. | 380 // username input field name. |
408 // Exact origin match gives an addition of 32 (1 << 5) + # of matching url | 381 // Exact origin match gives an addition of 32 (1 << 5) + # of matching url |
409 // dirs. | 382 // dirs. |
(...skipping 30 matching lines...) Expand all Loading... |
440 if (candidate.password_element == observed_form_.password_element) | 413 if (candidate.password_element == observed_form_.password_element) |
441 score += 1 << 2; | 414 score += 1 << 2; |
442 if (candidate.submit_element == observed_form_.submit_element) | 415 if (candidate.submit_element == observed_form_.submit_element) |
443 score += 1 << 1; | 416 score += 1 << 1; |
444 if (candidate.username_element == observed_form_.username_element) | 417 if (candidate.username_element == observed_form_.username_element) |
445 score += 1 << 0; | 418 score += 1 << 0; |
446 } | 419 } |
447 | 420 |
448 return score; | 421 return score; |
449 } | 422 } |
OLD | NEW |