diff options
22 files changed, 504 insertions, 23 deletions
diff --git a/chrome/browser/autofill/autofill_external_delegate.cc b/chrome/browser/autofill/autofill_external_delegate.cc index 13f0b7d..ff94bd4 100644 --- a/chrome/browser/autofill/autofill_external_delegate.cc +++ b/chrome/browser/autofill/autofill_external_delegate.cc @@ -16,6 +16,16 @@ using content::RenderViewHost; +namespace { + +// The value to give as the unique id for all warnings. +const int kWarningId = -1; + +// The value to give as the unique id for all password entries. +const int kPasswordEntryId = -2; + +} // namespace + AutofillExternalDelegate::~AutofillExternalDelegate() { } @@ -24,6 +34,8 @@ AutofillExternalDelegate::AutofillExternalDelegate( AutofillManager* autofill_manager) : tab_contents_wrapper_(tab_contents_wrapper), autofill_manager_(autofill_manager), + password_autofill_manager_( + tab_contents_wrapper ? tab_contents_wrapper->web_contents() : NULL), autofill_query_id_(0), display_warning_if_disabled_(false), has_shown_autofill_popup_for_current_edit_(false), @@ -33,9 +45,13 @@ AutofillExternalDelegate::AutofillExternalDelegate( void AutofillExternalDelegate::SelectAutofillSuggestionAtIndex(int unique_id, int list_index) { + if (password_autofill_manager_.DidSelectAutofillSuggestion( + autofill_query_field_)) + return; + if (list_index == suggestions_options_index_ || list_index == suggestions_clear_index_ || - unique_id == -1) + unique_id == kWarningId) return; FillAutofillFormData(unique_id, true); @@ -81,7 +97,7 @@ void AutofillExternalDelegate::OnSuggestionsReturned( v.assign(1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED)); l.assign(1, string16()); i.assign(1, string16()); - ids.assign(1, -1); + ids.assign(1, kWarningId); } else if (ids[0] < 0 && ids.size() > 1) { // If we received a warning instead of suggestions from autofill but regular // suggestions from autocomplete, don't show the autofill warning. @@ -136,6 +152,24 @@ void AutofillExternalDelegate::OnSuggestionsReturned( has_shown_autofill_popup_for_current_edit_ |= has_autofill_item; } +void AutofillExternalDelegate::OnShowPasswordSuggestions( + const std::vector<string16>& suggestions, + const webkit::forms::FormField& field, + const gfx::Rect& bounds) { + autofill_query_field_ = field; + + if (suggestions.empty()) { + HideAutofillPopup(); + return; + } + + SetBounds(bounds); + + std::vector<string16> empty(suggestions.size()); + std::vector<int> password_ids(suggestions.size(), kPasswordEntryId); + ApplyAutofillSuggestions(suggestions, empty, empty, password_ids, -1); +} + void AutofillExternalDelegate::DidEndTextFieldEditing() { has_shown_autofill_popup_for_current_edit_ = false; } @@ -145,13 +179,9 @@ bool AutofillExternalDelegate::DidAcceptAutofillSuggestions( int unique_id, unsigned index) { // If the selected element is a warning we don't want to do anything. - if (unique_id < 0) + if (unique_id == kWarningId) return false; - // TODO(csharp): Add the password autofill manager. - // if (password_autofill_manager_->DidAcceptAutofillSuggestion(node, value)) - // return; - if (suggestions_options_index_ != -1 && index == static_cast<unsigned>(suggestions_options_index_)) { // User selected 'Autofill Options'. @@ -162,8 +192,12 @@ bool AutofillExternalDelegate::DidAcceptAutofillSuggestions( RenderViewHost* host = tab_contents_wrapper_->web_contents()->GetRenderViewHost(); host->Send(new AutofillMsg_ClearForm(host->GetRoutingID())); + } else if (password_autofill_manager_.DidAcceptAutofillSuggestion( + autofill_query_field_, value)) { + // DidAcceptAutofillSuggestion has already handled the work to fill in + // the page as required. } else if (!unique_id) { - // User selected an Autocomplete entry, so we fill directly. + // User selected an Autocomplete, so we fill directly. RenderViewHost* host = tab_contents_wrapper_->web_contents()->GetRenderViewHost(); host->Send(new AutofillMsg_SetNodeText( @@ -179,6 +213,10 @@ bool AutofillExternalDelegate::DidAcceptAutofillSuggestions( } void AutofillExternalDelegate::ClearPreviewedForm() { + if (password_autofill_manager_.DidClearAutofillSelection( + autofill_query_field_)) + return; + RenderViewHost* host = tab_contents_wrapper_->web_contents()->GetRenderViewHost(); host->Send(new AutofillMsg_ClearPreviewedForm(host->GetRoutingID())); @@ -191,6 +229,18 @@ void AutofillExternalDelegate::HideAutofillPopup() { HideAutofillPopupInternal(); } +void AutofillExternalDelegate::Reset() { + HideAutofillPopup(); + + password_autofill_manager_.Reset(); +} + +void AutofillExternalDelegate::AddPasswordFormMapping( + const webkit::forms::FormField& form, + const webkit::forms::PasswordFormFillData& fill_data) { + password_autofill_manager_.AddPasswordFormMapping(form, fill_data); +} + void AutofillExternalDelegate::FillAutofillFormData(int unique_id, bool is_preview) { RenderViewHost* host = diff --git a/chrome/browser/autofill/autofill_external_delegate.h b/chrome/browser/autofill/autofill_external_delegate.h index 11a5341..18aa57c 100644 --- a/chrome/browser/autofill/autofill_external_delegate.h +++ b/chrome/browser/autofill/autofill_external_delegate.h @@ -10,8 +10,10 @@ #include "base/compiler_specific.h" #include "base/string16.h" +#include "chrome/browser/autofill/password_autofill_manager.h" #include "webkit/forms/form_data.h" #include "webkit/forms/form_field.h" +#include "webkit/forms/password_form_dom_manager.h" class AutofillManager; class TabContentsWrapper; @@ -57,6 +59,11 @@ class AutofillExternalDelegate { const std::vector<string16>& autofill_icons, const std::vector<int>& autofill_unique_ids); + // Show password suggestions in the popup. + void OnShowPasswordSuggestions(const std::vector<string16>& suggestions, + const webkit::forms::FormField& field, + const gfx::Rect& bounds); + // Inform the delegate that the text field editing has ended, this is // used to help record the metrics of when a new popup is shown. void DidEndTextFieldEditing(); @@ -73,6 +80,15 @@ class AutofillExternalDelegate { // Hide the Autofill poup. virtual void HideAutofillPopup(); + // Returns the delegate to its starting state by removing any page specific + // values or settings. + void Reset(); + + // Inform the Password Manager of a filled form. + void AddPasswordFormMapping( + const webkit::forms::FormField& form, + const webkit::forms::PasswordFormFillData& fill_data); + // Platforms that wish to implement an external Autofill delegate // MUST implement this. The 1st arg is the tab contents that owns // this delegate; the second is the Autofill manager owned by the @@ -102,6 +118,9 @@ class AutofillExternalDelegate { // Handle platform-dependent hiding. virtual void HideAutofillPopupInternal() = 0; + // Set the bounds of the Autofill element being worked with. + virtual void SetBounds(const gfx::Rect& bounds) = 0; + private: // Fills the form with the Autofill data corresponding to |unique_id|. // If |is_preview| is true then this is just a preview to show the user what @@ -112,6 +131,9 @@ class AutofillExternalDelegate { TabContentsWrapper* tab_contents_wrapper_; // weak; owns me. AutofillManager* autofill_manager_; // weak. + // Password Autofill manager, handles all password-related Autofilling. + PasswordAutofillManager password_autofill_manager_; + // The ID of the last request sent for form field Autofill. Used to ignore // out of date responses. int autofill_query_id_; diff --git a/chrome/browser/autofill/autofill_external_delegate_gtk.cc b/chrome/browser/autofill/autofill_external_delegate_gtk.cc index 4c7bbfa..e140d3b 100644 --- a/chrome/browser/autofill/autofill_external_delegate_gtk.cc +++ b/chrome/browser/autofill/autofill_external_delegate_gtk.cc @@ -61,6 +61,11 @@ void AutofillExternalDelegateGtk::ApplyAutofillSuggestions( separator_index); } +void AutofillExternalDelegateGtk::SetBounds(const gfx::Rect& bounds) { + CreateViewIfNeeded(); + view_->set_element_bounds(bounds); +} + void AutofillExternalDelegateGtk::CreateViewIfNeeded() { if (view_.get()) return; diff --git a/chrome/browser/autofill/autofill_external_delegate_gtk.h b/chrome/browser/autofill/autofill_external_delegate_gtk.h index c2f9185..aada4f0 100644 --- a/chrome/browser/autofill/autofill_external_delegate_gtk.h +++ b/chrome/browser/autofill/autofill_external_delegate_gtk.h @@ -40,6 +40,7 @@ class AutofillExternalDelegateGtk : public AutofillExternalDelegate { const std::vector<string16>& autofill_icons, const std::vector<int>& autofill_unique_ids, int separator_index) OVERRIDE; + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; private: // Create a valid view to display the autofill results if one doesn't diff --git a/chrome/browser/autofill/autofill_manager.cc b/chrome/browser/autofill/autofill_manager.cc index a85cd88..902cb5f 100644 --- a/chrome/browser/autofill/autofill_manager.cc +++ b/chrome/browser/autofill/autofill_manager.cc @@ -60,6 +60,7 @@ #include "webkit/forms/form_data.h" #include "webkit/forms/form_data_predictions.h" #include "webkit/forms/form_field.h" +#include "webkit/forms/password_form_dom_manager.h" using base::TimeTicks; using content::BrowserThread; @@ -267,6 +268,10 @@ void AutofillManager::DidNavigateMainFrame( Reset(); } +bool AutofillManager::HasExternalDelegate() { + return external_delegate_ != NULL; +} + bool AutofillManager::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(AutofillManager, message) @@ -292,6 +297,10 @@ bool AutofillManager::OnMessageReceived(const IPC::Message& message) { OnHideAutofillPopup) IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowPasswordGenerationPopup, OnShowPasswordGenerationPopup) + IPC_MESSAGE_HANDLER(AutofillHostMsg_AddPasswordFormMapping, + OnAddPasswordFormMapping) + IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowPasswordSuggestions, + OnShowPasswordSuggestions) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -679,6 +688,21 @@ void AutofillManager::OnShowPasswordGenerationPopup(const gfx::Rect& bounds) { #endif // #if defined(OS_ANDROID) } +void AutofillManager::OnAddPasswordFormMapping( + const webkit::forms::FormField& form, + const webkit::forms::PasswordFormFillData& fill_data) { + if (external_delegate_) + external_delegate_->AddPasswordFormMapping(form, fill_data); +} + +void AutofillManager::OnShowPasswordSuggestions( + const webkit::forms::FormField& field, + const gfx::Rect& bounds, + const std::vector<string16>& suggestions) { + if (external_delegate_) + external_delegate_->OnShowPasswordSuggestions(suggestions, field, bounds); +} + void AutofillManager::OnLoadedServerPredictions( const std::string& response_xml) { // Parse and store the server predictions. @@ -787,6 +811,9 @@ void AutofillManager::Reset() { user_did_edit_autofilled_field_ = false; forms_loaded_timestamp_ = TimeTicks(); initial_interaction_timestamp_ = TimeTicks(); + + if (external_delegate_) + external_delegate_->Reset(); } AutofillManager::AutofillManager(TabContentsWrapper* tab_contents, diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index 86203c5..11bfbc0 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/autofill_manager.h @@ -54,6 +54,7 @@ namespace webkit { namespace forms { struct FormData; struct FormField; +struct PasswordFormFillData; } } @@ -77,6 +78,9 @@ class AutofillManager : public content::WebContentsObserver, external_delegate_ = delegate; } + // Used to say if this class has an external delegate that it is using. + bool HasExternalDelegate(); + // Called from our external delegate so they cannot be private. virtual void OnFillAutofillFormData(int query_id, const webkit::forms::FormData& form, @@ -187,6 +191,12 @@ class AutofillManager : public content::WebContentsObserver, bool display_warning); void OnDidEndTextFieldEditing(); void OnHideAutofillPopup(); + void OnAddPasswordFormMapping( + const webkit::forms::FormField& form, + const webkit::forms::PasswordFormFillData& fill_data); + void OnShowPasswordSuggestions(const webkit::forms::FormField& field, + const gfx::Rect& bounds, + const std::vector<string16>& suggestions); // Fills |host| with the RenderViewHost for this tab. // Returns false if Autofill is disabled or if the host is unavailable. diff --git a/chrome/browser/autofill/password_autofill_manager.cc b/chrome/browser/autofill/password_autofill_manager.cc new file mode 100644 index 0000000..6d22a7f --- /dev/null +++ b/chrome/browser/autofill/password_autofill_manager.cc @@ -0,0 +1,97 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/autofill/password_autofill_manager.h" +#include "chrome/common/autofill_messages.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "ui/base/keycodes/keyboard_codes.h" + +//////////////////////////////////////////////////////////////////////////////// +// PasswordAutofillManager, public: + +PasswordAutofillManager::PasswordAutofillManager( + content::WebContents* web_contents) : web_contents_(web_contents) { +} + +PasswordAutofillManager::~PasswordAutofillManager() { +} + +bool PasswordAutofillManager::DidAcceptAutofillSuggestion( + const webkit::forms::FormField& field, + const string16& value) { + webkit::forms::PasswordFormFillData password; + if (!FindLoginInfo(field, &password)) + return false; + + if (WillFillUserNameAndPassword(value, password)) { + if (web_contents_) { + content::RenderViewHost* render_view_host = + web_contents_->GetRenderViewHost(); + render_view_host->Send(new AutofillMsg_AcceptPasswordAutofillSuggestion( + render_view_host->GetRoutingID(), + value)); + } + return true; + } + + return false; +} + +bool PasswordAutofillManager::DidSelectAutofillSuggestion( + const webkit::forms::FormField& field) { + webkit::forms::FormField input; + webkit::forms::PasswordFormFillData password; + return FindLoginInfo(field, &password); +} + +bool PasswordAutofillManager::DidClearAutofillSelection( + const webkit::forms::FormField& field) { + webkit::forms::FormField input; + webkit::forms::PasswordFormFillData password; + return FindLoginInfo(field, &password); +} + +void PasswordAutofillManager::AddPasswordFormMapping( + const webkit::forms::FormField& username_element, + const webkit::forms::PasswordFormFillData& password) { + login_to_password_info_[username_element] = password; +} + +void PasswordAutofillManager::Reset() { + login_to_password_info_.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// +// PasswordAutofillManager, private: + +bool PasswordAutofillManager::WillFillUserNameAndPassword( + const string16& current_username, + const webkit::forms::PasswordFormFillData& fill_data) { + // Look for any suitable matches to current field text. + if (fill_data.basic_data.fields[0].value == current_username) { + return true; + } else { + // Scan additional logins for a match. + webkit::forms::PasswordFormFillData::LoginCollection::const_iterator iter; + for (iter = fill_data.additional_logins.begin(); + iter != fill_data.additional_logins.end(); ++iter) { + if (iter->first == current_username) + return true; + } + } + + return false; +} + +bool PasswordAutofillManager::FindLoginInfo( + const webkit::forms::FormField& field, + webkit::forms::PasswordFormFillData* found_password) { + LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(field); + if (iter == login_to_password_info_.end()) + return false; + + *found_password = iter->second; + return true; +} diff --git a/chrome/browser/autofill/password_autofill_manager.h b/chrome/browser/autofill/password_autofill_manager.h new file mode 100644 index 0000000..46d4b8e --- /dev/null +++ b/chrome/browser/autofill/password_autofill_manager.h @@ -0,0 +1,81 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_AUTOFILL_PASSWORD_AUTOFILL_MANAGER_H_ +#define CHROME_BROWSER_AUTOFILL_PASSWORD_AUTOFILL_MANAGER_H_ +#pragma once + +// This file was contains some repeated code from +// chrome/renderer/autofill/password_autofill_manager because as we move to the +// new Autofill UI we needs these functions in both the browser and renderer. +// Once the move is completed the repeated code in the renderer half should be +// removed. +// http://crbug.com/51644 + +#include <map> + +#include "webkit/forms/password_form_dom_manager.h" + +namespace content { +class WebContents; +} // namespace content + +// This class is responsible for filling password forms. +class PasswordAutofillManager { + public: + explicit PasswordAutofillManager(content::WebContents* web_contents); + virtual ~PasswordAutofillManager(); + + // Fills the password associated with user name |value|. Returns true if the + // username and password fields were filled, false otherwise. + bool DidAcceptAutofillSuggestion(const webkit::forms::FormField& field, + const string16& value); + + // A no-op. No filling happens for selection. But this method returns + // true when |node| is fillable by password Autofill. + bool DidSelectAutofillSuggestion(const webkit::forms::FormField& field); + + // A no-op. Password forms are not previewed, so they do not need to be + // cleared when the selection changes. However, this method returns + // true when |node| is fillable by password Autofill. + bool DidClearAutofillSelection(const webkit::forms::FormField& field); + + // Invoked when a password mapping is added. + void AddPasswordFormMapping( + const webkit::forms::FormField& username_element, + const webkit::forms::PasswordFormFillData& password); + + // Invoked to clear any page specific cached values. + void Reset(); + + private: + // TODO(csharp): Modify the AutofillExternalDeletegate code so that it can + // figure out if a entry is a password one without using this mapping. + // crbug.com/118601 + typedef std::map<webkit::forms::FormField, + webkit::forms::PasswordFormFillData> + LoginToPasswordInfoMap; + + // Returns true if |current_username| matches a username for one of the + // login mappings in |password|. + bool WillFillUserNameAndPassword( + const string16& current_username, + const webkit::forms::PasswordFormFillData& password); + + // Finds login information for a |node| that was previously filled. + bool FindLoginInfo(const webkit::forms::FormField& field, + webkit::forms::PasswordFormFillData* found_password); + + // The logins we have filled so far with their associated info. + LoginToPasswordInfoMap login_to_password_info_; + + // We only need the RenderViewHost pointer in WebContents, but if we attempt + // to just store RenderViewHost on creation, it becomes invalid once we start + // using it. By having the WebContents we can always get a valid pointer. + content::WebContents* web_contents_; // Weak reference. + + DISALLOW_COPY_AND_ASSIGN(PasswordAutofillManager); +}; + +#endif // CHROME_BROWSER_AUTOFILL_PASSWORD_AUTOFILL_MANAGER_H_ diff --git a/chrome/browser/autofill/password_autofill_manager_unittest.cc b/chrome/browser/autofill/password_autofill_manager_unittest.cc new file mode 100644 index 0000000..325f31e --- /dev/null +++ b/chrome/browser/autofill/password_autofill_manager_unittest.cc @@ -0,0 +1,94 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/compiler_specific.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/autofill/password_autofill_manager.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// The name of the username/password element in the form. +const char* const kUsernameName = "username"; +const char* const kInvalidUsername = "no-username"; +const char* const kPasswordName = "password"; + +const char* const kAliceUsername = "alice"; +const char* const kAlicePassword = "password"; + +const char* const kValue = "password"; + +} // namespace + +class PasswordAutofillManagerTest : public testing::Test { + protected: + PasswordAutofillManagerTest() : password_autofill_manager_(NULL) {} + + virtual void SetUp() OVERRIDE { + // Add a preferred login and an additional login to the FillData. + string16 username1 = ASCIIToUTF16(kAliceUsername); + string16 password1 = ASCIIToUTF16(kAlicePassword); + + username_field_.name = ASCIIToUTF16(kUsernameName); + username_field_.value = username1; + fill_data_.basic_data.fields.push_back(username_field_); + + webkit::forms::FormField password_field; + password_field.name = ASCIIToUTF16(kPasswordName); + password_field.value = password1; + fill_data_.basic_data.fields.push_back(password_field); + + password_autofill_manager_.AddPasswordFormMapping(username_field_, + fill_data_); + } + + PasswordAutofillManager* password_autofill_manager() { + return &password_autofill_manager_; + } + + const webkit::forms::FormField& username_field() { return username_field_; } + + private: + webkit::forms::PasswordFormFillData fill_data_; + webkit::forms::FormField username_field_; + + PasswordAutofillManager password_autofill_manager_; +}; + +TEST_F(PasswordAutofillManagerTest, DidAcceptAutofillSuggestion) { + EXPECT_TRUE(password_autofill_manager()->DidAcceptAutofillSuggestion( + username_field(), ASCIIToUTF16(kAliceUsername))); + EXPECT_FALSE(password_autofill_manager()->DidAcceptAutofillSuggestion( + username_field(), ASCIIToUTF16(kInvalidUsername))); + + webkit::forms::FormField invalid_username_field; + invalid_username_field.name = ASCIIToUTF16(kInvalidUsername); + + EXPECT_FALSE(password_autofill_manager()->DidAcceptAutofillSuggestion( + invalid_username_field, ASCIIToUTF16(kAliceUsername))); + + password_autofill_manager()->Reset(); + EXPECT_FALSE(password_autofill_manager()->DidAcceptAutofillSuggestion( + username_field(), ASCIIToUTF16(kAliceUsername))); +} + +TEST_F(PasswordAutofillManagerTest, DidSelectAutofillSuggestion) { + EXPECT_TRUE(password_autofill_manager()->DidSelectAutofillSuggestion( + username_field())); + + password_autofill_manager()->Reset(); + + EXPECT_FALSE(password_autofill_manager()->DidSelectAutofillSuggestion( + username_field())); +} + +TEST_F(PasswordAutofillManagerTest, DidClearAutofillSelection) { + EXPECT_TRUE(password_autofill_manager()->DidClearAutofillSelection( + username_field())); + + password_autofill_manager()->Reset(); + + EXPECT_FALSE(password_autofill_manager()->DidClearAutofillSelection( + username_field())); +} diff --git a/chrome/browser/autofill/test_autofill_external_delegate.cc b/chrome/browser/autofill/test_autofill_external_delegate.cc index 9f076f1..c461bb4 100644 --- a/chrome/browser/autofill/test_autofill_external_delegate.cc +++ b/chrome/browser/autofill/test_autofill_external_delegate.cc @@ -24,3 +24,5 @@ void TestAutofillExternalDelegate::OnQueryPlatformSpecific( const gfx::Rect& bounds) {} void TestAutofillExternalDelegate::HideAutofillPopupInternal() {} + +void TestAutofillExternalDelegate::SetBounds(const gfx::Rect& bounds) {} diff --git a/chrome/browser/autofill/test_autofill_external_delegate.h b/chrome/browser/autofill/test_autofill_external_delegate.h index 771fcf8..6b95d85 100644 --- a/chrome/browser/autofill/test_autofill_external_delegate.h +++ b/chrome/browser/autofill/test_autofill_external_delegate.h @@ -35,6 +35,8 @@ class TestAutofillExternalDelegate : public AutofillExternalDelegate { virtual void HideAutofillPopupInternal() OVERRIDE; + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; + private: DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate); }; diff --git a/chrome/browser/password_manager_delegate_impl.cc b/chrome/browser/password_manager_delegate_impl.cc index 5a3b572..f0caecb 100644 --- a/chrome/browser/password_manager_delegate_impl.cc +++ b/chrome/browser/password_manager_delegate_impl.cc @@ -6,6 +6,7 @@ #include "base/memory/singleton.h" #include "base/metrics/histogram.h" +#include "chrome/browser/autofill/autofill_manager.h" #include "chrome/browser/infobars/infobar_tab_helper.h" #include "chrome/browser/password_manager/password_form_manager.h" #include "chrome/browser/password_manager/password_manager.h" @@ -123,10 +124,13 @@ SavePasswordInfoBarDelegate::AsSavePasswordInfoBarDelegate() { void PasswordManagerDelegateImpl::FillPasswordForm( const webkit::forms::PasswordFormFillData& form_data) { + bool disable_popup = tab_contents_->autofill_manager()->HasExternalDelegate(); + tab_contents_->web_contents()->GetRenderViewHost()->Send( new AutofillMsg_FillPasswordForm( tab_contents_->web_contents()->GetRenderViewHost()->GetRoutingID(), - form_data)); + form_data, + disable_popup)); } void PasswordManagerDelegateImpl::AddSavePasswordInfoBarIfPermitted( diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 9e1eeee..8821250 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -199,8 +199,10 @@ 'browser/autofill/form_structure.h', 'browser/autofill/name_field.cc', 'browser/autofill/name_field.h', - 'browser/autofill/password_generator.cc', - 'browser/autofill/password_generator.h', + 'browser/autofill/password_autofill_manager.cc', + 'browser/autofill/password_autofill_manager.h', + 'browser/autofill/password_generator.cc', + 'browser/autofill/password_generator.h', 'browser/autofill/personal_data_manager.cc', 'browser/autofill/personal_data_manager.h', 'browser/autofill/personal_data_manager_factory.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index b89dfb5..80b5f3dd 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1270,7 +1270,8 @@ 'browser/autofill/form_field_unittest.cc', 'browser/autofill/form_structure_unittest.cc', 'browser/autofill/name_field_unittest.cc', - 'browser/autofill/password_generator_unittest.cc', + 'browser/autofill/password_autofill_manager_unittest.cc', + 'browser/autofill/password_generator_unittest.cc', 'browser/autofill/personal_data_manager_unittest.cc', 'browser/autofill/phone_field_unittest.cc', 'browser/autofill/phone_number_unittest.cc', diff --git a/chrome/common/autofill_messages.h b/chrome/common/autofill_messages.h index 473271a..0297c07 100644 --- a/chrome/common/autofill_messages.h +++ b/chrome/common/autofill_messages.h @@ -82,9 +82,11 @@ IPC_MESSAGE_ROUTED2(AutofillMsg_FormDataFilled, webkit::forms::FormData /* form data */) // Fill a password form and prepare field autocomplete for multiple -// matching logins. -IPC_MESSAGE_ROUTED1(AutofillMsg_FillPasswordForm, - webkit::forms::PasswordFormFillData) +// matching logins. Lets the renderer know if it should disable the popup +// because the browser process will own the popup UI. +IPC_MESSAGE_ROUTED2(AutofillMsg_FillPasswordForm, + webkit::forms::PasswordFormFillData, /* the fill form data*/ + bool /* disable popup */ ) // Send the heuristic and server field type predictions to the renderer. IPC_MESSAGE_ROUTED1( @@ -121,6 +123,10 @@ IPC_MESSAGE_ROUTED1(AutofillMsg_GeneratedPasswordAccepted, IPC_MESSAGE_ROUTED1(AutofillMsg_PasswordSyncEnabled, bool /* is_enabled */) +// Tells the renderer that the password field has accept the suggestion. +IPC_MESSAGE_ROUTED1(AutofillMsg_AcceptPasswordAutofillSuggestion, + string16 /* username value*/) + // Autofill messages sent from the renderer to the browser. // Notification that forms have been seen that are candidates for @@ -197,3 +203,15 @@ IPC_MESSAGE_ROUTED0(AutofillHostMsg_HideAutofillPopup) // coordinate system. IPC_MESSAGE_ROUTED1(AutofillHostMsg_ShowPasswordGenerationPopup, gfx::Rect /* source location */) + +// Instruct the browser that a password mapping has been found for a field. +IPC_MESSAGE_ROUTED2(AutofillHostMsg_AddPasswordFormMapping, + webkit::forms::FormField, /* the user name field */ + webkit::forms::PasswordFormFillData /* password pairings */) + +// Instruct the browser to show a popup with the following suggestions from the +// password manager. +IPC_MESSAGE_ROUTED3(AutofillHostMsg_ShowPasswordSuggestions, + webkit::forms::FormField /* the form field */, + gfx::Rect /* input field bounds, window-relative */, + std::vector<string16> /* suggestions */) diff --git a/chrome/renderer/autofill/autofill_agent.cc b/chrome/renderer/autofill/autofill_agent.cc index d51cc48..294ba19 100644 --- a/chrome/renderer/autofill/autofill_agent.cc +++ b/chrome/renderer/autofill/autofill_agent.cc @@ -86,6 +86,8 @@ bool AutofillAgent::OnMessageReceived(const IPC::Message& message) { OnClearPreviewedForm) IPC_MESSAGE_HANDLER(AutofillMsg_SetNodeText, OnSetNodeText) + IPC_MESSAGE_HANDLER(AutofillMsg_AcceptPasswordAutofillSuggestion, + OnAcceptPasswordAutofillSuggestion) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -230,8 +232,10 @@ void AutofillAgent::textFieldDidChange(const WebInputElement& element) { } void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) { - if (password_autofill_manager_->TextDidChangeInTextField(element)) + if (password_autofill_manager_->TextDidChangeInTextField(element)) { + autofill_query_element_ = element; return; + } ShowSuggestions(element, false, true, false); @@ -245,8 +249,10 @@ void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) { void AutofillAgent::textFieldDidReceiveKeyDown(const WebInputElement& element, const WebKeyboardEvent& event) { - if (password_autofill_manager_->TextFieldHandlingKeyDown(element, event)) + if (password_autofill_manager_->TextFieldHandlingKeyDown(element, event)) { + autofill_query_element_ = element; return; + } if (event.windowsKeyCode == ui::VKEY_DOWN || event.windowsKeyCode == ui::VKEY_UP) @@ -398,6 +404,16 @@ void AutofillAgent::OnSetNodeText(const string16& value) { SetNodeText(value, &autofill_query_element_); } +void AutofillAgent::OnAcceptPasswordAutofillSuggestion(const string16& value) { + // We need to make sure this is handled here because the browser process + // skipped it handling because it believed it would be handled here. If it + // isn't handled here then the browser logic needs to be updated. + bool handled = password_autofill_manager_->DidAcceptAutofillSuggestion( + autofill_query_element_, + value); + DCHECK(handled); +} + void AutofillAgent::ShowSuggestions(const WebInputElement& element, bool autofill_on_empty_values, bool requires_caret_at_end, diff --git a/chrome/renderer/autofill/autofill_agent.h b/chrome/renderer/autofill/autofill_agent.h index d02a903..843ee6c 100644 --- a/chrome/renderer/autofill/autofill_agent.h +++ b/chrome/renderer/autofill/autofill_agent.h @@ -110,6 +110,7 @@ class AutofillAgent : public content::RenderViewObserver, void OnSetAutofillActionPreview(); void OnClearPreviewedForm(); void OnSetNodeText(const string16& value); + void OnAcceptPasswordAutofillSuggestion(const string16& value); // Called in a posted task by textFieldDidChange() to work-around a WebKit bug // http://bugs.webkit.org/show_bug.cgi?id=16976 diff --git a/chrome/renderer/autofill/password_autofill_manager.cc b/chrome/renderer/autofill/password_autofill_manager.cc index 26e6fd6f..2bbee31 100644 --- a/chrome/renderer/autofill/password_autofill_manager.cc +++ b/chrome/renderer/autofill/password_autofill_manager.cc @@ -8,6 +8,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "chrome/common/autofill_messages.h" +#include "chrome/renderer/autofill/form_autofill_util.h" #include "content/public/renderer/render_view.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebDocument.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebElement.h" @@ -204,6 +205,7 @@ namespace autofill { PasswordAutofillManager::PasswordAutofillManager( content::RenderView* render_view) : content::RenderViewObserver(render_view), + disable_popup_(false), ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { } @@ -293,6 +295,10 @@ bool PasswordAutofillManager::TextDidChangeInTextField( bool PasswordAutofillManager::TextFieldHandlingKeyDown( const WebKit::WebInputElement& element, const WebKit::WebKeyboardEvent& event) { + // If using the new Autofill UI that lives in the browser, it will handle + // keypresses before this function. This is not currently an issue but if + // the keys handled there or here change, this issue may appear. + LoginToPasswordInfoMap::iterator iter = login_to_password_info_.find(element); if (iter == login_to_password_info_.end()) return false; @@ -425,7 +431,10 @@ bool PasswordAutofillManager::InputElementLostFocus() { } void PasswordAutofillManager::OnFillPasswordForm( - const webkit::forms::PasswordFormFillData& form_data) { + const webkit::forms::PasswordFormFillData& form_data, + bool disable_popup) { + disable_popup_ = disable_popup; + FormElementsList forms; // We own the FormElements* in forms. FindFormElements(render_view()->GetWebView(), form_data.basic_data, &forms); @@ -458,6 +467,15 @@ void PasswordAutofillManager::OnFillPasswordForm( password_info.fill_data = form_data; password_info.password_field = password_element; login_to_password_info_[username_element] = password_info; + + webkit::forms::FormData form; + webkit::forms::FormField field; + FindFormAndFieldForInputElement( + username_element, &form, &field, REQUIRE_NONE); + Send(new AutofillHostMsg_AddPasswordFormMapping( + routing_id(), + field, + form_data)); } } @@ -492,6 +510,23 @@ bool PasswordAutofillManager::ShowSuggestionPopup( std::vector<string16> suggestions; GetSuggestions(fill_data, user_input.value(), &suggestions); + + if (disable_popup_) { + webkit::forms::FormData form; + webkit::forms::FormField field; + FindFormAndFieldForInputElement( + user_input, &form, &field, REQUIRE_NONE); + + WebKit::WebInputElement selected_element = user_input; + gfx::Rect bounding_box(selected_element.boundsInViewportSpace()); + Send(new AutofillHostMsg_ShowPasswordSuggestions(routing_id(), + field, + bounding_box, + suggestions)); + return !suggestions.empty(); + } + + if (suggestions.empty()) { webview->hidePopups(); return false; diff --git a/chrome/renderer/autofill/password_autofill_manager.h b/chrome/renderer/autofill/password_autofill_manager.h index 254571a89..0fd1906 100644 --- a/chrome/renderer/autofill/password_autofill_manager.h +++ b/chrome/renderer/autofill/password_autofill_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -75,7 +75,8 @@ class PasswordAutofillManager : public content::RenderViewObserver, virtual bool InputElementLostFocus() OVERRIDE; // RenderView IPC handlers: - void OnFillPasswordForm(const webkit::forms::PasswordFormFillData& form_data); + void OnFillPasswordForm(const webkit::forms::PasswordFormFillData& form_data, + bool disable_popup); // Scans the given frame for password forms and sends them up to the browser. // If |only_visible| is true, only forms visible in the layout are sent. @@ -114,6 +115,9 @@ class PasswordAutofillManager : public content::RenderViewObserver, // The logins we have filled so far with their associated info. LoginToPasswordInfoMap login_to_password_info_; + // Used to disable and hide the popup. + bool disable_popup_; + base::WeakPtrFactory<PasswordAutofillManager> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(PasswordAutofillManager); diff --git a/chrome/renderer/autofill/password_autofill_manager_browsertest.cc b/chrome/renderer/autofill/password_autofill_manager_browsertest.cc index 7d1dea0..d242e6d 100644 --- a/chrome/renderer/autofill/password_autofill_manager_browsertest.cc +++ b/chrome/renderer/autofill/password_autofill_manager_browsertest.cc @@ -65,7 +65,7 @@ class PasswordAutofillManagerTest : public ChromeRenderViewTest { // protected. void SimulateOnFillPasswordForm( const PasswordFormFillData& fill_data) { - AutofillMsg_FillPasswordForm msg(0, fill_data); + AutofillMsg_FillPasswordForm msg(0, fill_data, false); password_autofill_->OnMessageReceived(msg); } diff --git a/webkit/forms/form_field.cc b/webkit/forms/form_field.cc index 1c9a0a7..f32b830 100644 --- a/webkit/forms/form_field.cc +++ b/webkit/forms/form_field.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -44,6 +44,13 @@ bool FormField::operator!=(const FormField& field) const { return !operator==(field); } +bool FormField::operator<(const FormField& field) const { + if (label == field.label) + return name < field.name; + + return label < field.label; +} + std::ostream& operator<<(std::ostream& os, const FormField& field) { return os << UTF16ToUTF8(field.label) diff --git a/webkit/forms/form_field.h b/webkit/forms/form_field.h index d1b17c0..2f5f0b2 100644 --- a/webkit/forms/form_field.h +++ b/webkit/forms/form_field.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -25,6 +25,8 @@ struct WEBKIT_FORMS_EXPORT FormField { // ids. bool operator==(const FormField& field) const; bool operator!=(const FormField& field) const; + // Comparsion operator exposed for STL map. Uses label, then name to sort. + bool operator<(const FormField& field) const; string16 label; string16 name; |