diff options
author | csharp@chromium.org <csharp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-09 15:21:53 +0000 |
---|---|---|
committer | csharp@chromium.org <csharp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-09 15:21:53 +0000 |
commit | 6f001a6cf0c8e74345ebab5c2ddb65babe892cc2 (patch) | |
tree | fcb5d37e8c9a834a1ebd87eb7fde8c2b2750caa9 | |
parent | 6f7f89a4d8651e9916640100106b8dfe95d03c71 (diff) | |
download | chromium_src-6f001a6cf0c8e74345ebab5c2ddb65babe892cc2.zip chromium_src-6f001a6cf0c8e74345ebab5c2ddb65babe892cc2.tar.gz chromium_src-6f001a6cf0c8e74345ebab5c2ddb65babe892cc2.tar.bz2 |
Adding Mouse Support for new GTK Autofill
Adds the ability use the mouse to select an Autofill option and see the preview of an option.
BUG=51644
TEST=
Review URL: http://codereview.chromium.org/9235072
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@121234 0039d316-1c4b-4281-b951-d872f2087c98
20 files changed, 583 insertions, 157 deletions
diff --git a/chrome/browser/autocomplete_history_manager_unittest.cc b/chrome/browser/autocomplete_history_manager_unittest.cc index add04f0..d49f03d 100644 --- a/chrome/browser/autocomplete_history_manager_unittest.cc +++ b/chrome/browser/autocomplete_history_manager_unittest.cc @@ -8,7 +8,7 @@ #include "base/string16.h" #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete_history_manager.h" -#include "chrome/browser/autofill/autofill_external_delegate.h" +#include "chrome/browser/autofill/test_autofill_external_delegate.h" #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" #include "chrome/browser/webdata/web_data_service.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" @@ -139,17 +139,18 @@ TEST_F(AutocompleteHistoryManagerTest, SearchField) { namespace { -class MockAutofillExternalDelegate : public AutofillExternalDelegate { +class MockAutofillExternalDelegate : public TestAutofillExternalDelegate { public: explicit MockAutofillExternalDelegate(TabContentsWrapper* wrapper) - : AutofillExternalDelegate(wrapper, NULL) {} + : TestAutofillExternalDelegate(wrapper, NULL) {} virtual ~MockAutofillExternalDelegate() {} - virtual void OnQuery(int query_id, - const webkit::forms::FormData& form, - const webkit::forms::FormField& field, - const gfx::Rect& bounds, - bool display_warning) OVERRIDE {} + virtual void ApplyAutofillSuggestions( + const std::vector<string16>& autofill_values, + const std::vector<string16>& autofill_labels, + const std::vector<string16>& autofill_icons, + const std::vector<int>& autofill_unique_ids, + int separator_index) OVERRIDE {}; MOCK_METHOD5(OnSuggestionsReturned, void(int query_id, @@ -158,22 +159,6 @@ class MockAutofillExternalDelegate : public AutofillExternalDelegate { const std::vector<string16>& autofill_icons, const std::vector<int>& autofill_unique_ids)); - virtual void HideAutofillPopup() OVERRIDE {} - - - virtual void ApplyAutofillSuggestions( - const std::vector<string16>& autofill_values, - const std::vector<string16>& autofill_labels, - const std::vector<string16>& autofill_icons, - const std::vector<int>& autofill_unique_ids, - int separator_index) OVERRIDE {} - - virtual void OnQueryPlatformSpecific( - int query_id, - const webkit::forms::FormData& form, - const webkit::forms::FormField& field, - const gfx::Rect& bounds) OVERRIDE {} - private: DISALLOW_COPY_AND_ASSIGN(MockAutofillExternalDelegate); }; diff --git a/chrome/browser/autofill/autofill_external_delegate.cc b/chrome/browser/autofill/autofill_external_delegate.cc index f3a7e94..491ffee 100644 --- a/chrome/browser/autofill/autofill_external_delegate.cc +++ b/chrome/browser/autofill/autofill_external_delegate.cc @@ -24,15 +24,19 @@ AutofillExternalDelegate::AutofillExternalDelegate( autofill_manager_(autofill_manager), autofill_query_id_(0), display_warning_if_disabled_(false), - has_shown_autofill_popup_for_current_edit_(false) { + has_shown_autofill_popup_for_current_edit_(false), + suggestions_clear_index_(-1), + suggestions_options_index_(-1) { } -void AutofillExternalDelegate::SelectAutofillSuggestionAtIndex(int listIndex) { - RenderViewHost* host = - tab_contents_wrapper_->web_contents()->GetRenderViewHost(); - host->Send(new AutofillMsg_SelectAutofillSuggestionAtIndex( - host->routing_id(), - listIndex)); +void AutofillExternalDelegate::SelectAutofillSuggestionAtIndex(int unique_id, + int list_index) { + if (list_index == suggestions_options_index_ || + list_index == suggestions_clear_index_ || + unique_id == -1) + return; + + FillAutofillFormData(unique_id, true); } void AutofillExternalDelegate::OnQuery(int query_id, @@ -40,6 +44,7 @@ void AutofillExternalDelegate::OnQuery(int query_id, const webkit::forms::FormField& field, const gfx::Rect& bounds, bool display_warning_if_disabled) { + autofill_query_form_ = form; autofill_query_field_ = field; display_warning_if_disabled_ = display_warning_if_disabled; autofill_query_id_ = query_id; @@ -47,10 +52,6 @@ void AutofillExternalDelegate::OnQuery(int query_id, OnQueryPlatformSpecific(query_id, form, field, bounds); } -void AutofillExternalDelegate::DidEndTextFieldEditing() { - has_shown_autofill_popup_for_current_edit_ = false; -} - void AutofillExternalDelegate::OnSuggestionsReturned( int query_id, const std::vector<string16>& values, @@ -110,6 +111,7 @@ void AutofillExternalDelegate::OnSuggestionsReturned( l.push_back(string16()); i.push_back(string16()); ids.push_back(0); + suggestions_clear_index_ = v.size() - 1; separator_index = v.size() - 1; } @@ -119,6 +121,7 @@ void AutofillExternalDelegate::OnSuggestionsReturned( l.push_back(string16()); i.push_back(string16()); ids.push_back(0); + suggestions_options_index_ = v.size() - 1; separator_index = values.size(); } @@ -131,6 +134,77 @@ void AutofillExternalDelegate::OnSuggestionsReturned( has_shown_autofill_popup_for_current_edit_ |= has_autofill_item; } +void AutofillExternalDelegate::DidEndTextFieldEditing() { + has_shown_autofill_popup_for_current_edit_ = false; +} + +void AutofillExternalDelegate::DidAcceptAutofillSuggestions(string16 value, + int unique_id, + unsigned index) { + // If the selected element is a warning we don't want to do anything. + if (unique_id < 0) + return; + + // 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'. + autofill_manager_->OnShowAutofillDialog(); + } else if (suggestions_clear_index_ != -1 && + index == static_cast<unsigned>(suggestions_clear_index_)) { + // User selected 'Clear form'. + RenderViewHost* host = + tab_contents_wrapper_->web_contents()->GetRenderViewHost(); + host->Send(new AutofillMsg_ClearForm(host->routing_id())); + } else if (!unique_id) { + // User selected an Autocomplete entry, so we fill directly. + RenderViewHost* host = + tab_contents_wrapper_->web_contents()->GetRenderViewHost(); + host->Send(new AutofillMsg_SetNodeText( + host->routing_id(), + value)); + } else { + FillAutofillFormData(unique_id, false); + } + + HideAutofillPopup(); +} + +void AutofillExternalDelegate::ClearPreviewedForm() { + RenderViewHost* host = + tab_contents_wrapper_->web_contents()->GetRenderViewHost(); + host->Send(new AutofillMsg_ClearPreviewedForm(host->routing_id())); +} + +void AutofillExternalDelegate::HideAutofillPopup() { + suggestions_clear_index_ = -1; + suggestions_options_index_ = -1; + + HideAutofillPopupInternal(); +} + +void AutofillExternalDelegate::FillAutofillFormData(int unique_id, + bool is_preview) { + RenderViewHost* host = + tab_contents_wrapper_->web_contents()->GetRenderViewHost(); + + if (is_preview) { + host->Send(new AutofillMsg_SetAutofillActionPreview( + host->routing_id())); + } else { + host->Send(new AutofillMsg_SetAutofillActionFill( + host->routing_id())); + } + + // Fill the values for the whole form. + autofill_manager_->OnFillAutofillFormData(autofill_query_id_, + autofill_query_form_, + autofill_query_field_, + unique_id); +} // Add a "!defined(OS_YOUROS) for each platform that implements this // in an autofill_external_delegate_YOUROS.cc. Currently there are diff --git a/chrome/browser/autofill/autofill_external_delegate.h b/chrome/browser/autofill/autofill_external_delegate.h index 20d15bb..6953594 100644 --- a/chrome/browser/autofill/autofill_external_delegate.h +++ b/chrome/browser/autofill/autofill_external_delegate.h @@ -8,7 +8,9 @@ #include <vector> +#include "base/compiler_specific.h" #include "base/string16.h" +#include "webkit/forms/form_data.h" #include "webkit/forms/form_field.h" class AutofillManager; @@ -18,12 +20,6 @@ namespace gfx { class Rect; } -namespace webkit { -namespace forms { -struct FormData; -} -} - // TODO(csharp): A lot of the logic in this class is copied from autofillagent. // Once Autofill is moved out of WebKit this class should be the only home for // this logic. See http://crbug.com/51644 @@ -37,7 +33,7 @@ class AutofillExternalDelegate { // When using an external Autofill delegate. Allows Chrome to tell // WebKit which Autofill selection has been chosen. // TODO(jrg): add feedback mechanism for hover on relevant platforms. - void SelectAutofillSuggestionAtIndex(int listIndex); + virtual void SelectAutofillSuggestionAtIndex(int unique_id, int list_index); // Records and associates a query_id with web form data. Called // when the renderer posts an Autofill query to the browser. |bounds| @@ -61,8 +57,20 @@ class AutofillExternalDelegate { const std::vector<string16>& autofill_icons, const std::vector<int>& autofill_unique_ids); + // 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(); + + // Inform the delegate that an autofill suggestion have been chosen. + void DidAcceptAutofillSuggestions(string16 value, + int unique_id, + unsigned index); + + // Informs the delegate that the Autofill previewed form should be cleared. + virtual void ClearPreviewedForm(); + // Hide the Autofill poup. - virtual void HideAutofillPopup() = 0; + virtual void HideAutofillPopup(); // Platforms that wish to implement an external Autofill delegate // MUST implement this. The 1st arg is the tab contents that owns @@ -70,11 +78,6 @@ class AutofillExternalDelegate { // tab contents. static AutofillExternalDelegate* Create(TabContentsWrapper*, AutofillManager*); - - // 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(); - protected: explicit AutofillExternalDelegate(TabContentsWrapper* tab_contents_wrapper, AutofillManager* autofill_manager); @@ -95,7 +98,16 @@ class AutofillExternalDelegate { const webkit::forms::FormField& field, const gfx::Rect& bounds) = 0; + // Handle platform-dependent hiding. + virtual void HideAutofillPopupInternal() = 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 + // would be selected and if |is_preview| is false then the user has selected + // this data. + void FillAutofillFormData(int unique_id, bool is_preview); + TabContentsWrapper* tab_contents_wrapper_; // weak; owns me. AutofillManager* autofill_manager_; // weak. @@ -103,7 +115,8 @@ class AutofillExternalDelegate { // out of date responses. int autofill_query_id_; - // The current field selected by Autofill. + // The current form and field selected by Autofill. + webkit::forms::FormData autofill_query_form_; webkit::forms::FormField autofill_query_field_; // Should we display a warning if Autofill is disabled? @@ -113,6 +126,12 @@ class AutofillExternalDelegate { // currently editing? Used to keep track of state for metrics logging. bool has_shown_autofill_popup_for_current_edit_; + // The menu index of the "Clear" menu item. + int suggestions_clear_index_; + + // The menu index of the "Autofill options..." menu item. + int suggestions_options_index_; + DISALLOW_COPY_AND_ASSIGN(AutofillExternalDelegate); }; diff --git a/chrome/browser/autofill/autofill_external_delegate_gtk.cc b/chrome/browser/autofill/autofill_external_delegate_gtk.cc index 992dbd33..4c7bbfa 100644 --- a/chrome/browser/autofill/autofill_external_delegate_gtk.cc +++ b/chrome/browser/autofill/autofill_external_delegate_gtk.cc @@ -28,6 +28,17 @@ AutofillExternalDelegateGtk::AutofillExternalDelegateGtk( AutofillExternalDelegateGtk::~AutofillExternalDelegateGtk() { } +void AutofillExternalDelegateGtk::HideAutofillPopupInternal() { + if (!view_.get()) + return; + + view_->Hide(); + view_.reset(); + + GtkWidget* toplevel = gtk_widget_get_toplevel(tab_native_view_); + g_signal_handler_disconnect(toplevel, event_handler_id_); +} + void AutofillExternalDelegateGtk::OnQueryPlatformSpecific( int query_id, const webkit::forms::FormData& form, @@ -50,22 +61,12 @@ void AutofillExternalDelegateGtk::ApplyAutofillSuggestions( separator_index); } -void AutofillExternalDelegateGtk::HideAutofillPopup() { - if (!view_.get()) - return; - - view_->Hide(); - view_.reset(); - - GtkWidget* toplevel = gtk_widget_get_toplevel(tab_native_view_); - g_signal_handler_disconnect(toplevel, event_handler_id_); -} - void AutofillExternalDelegateGtk::CreateViewIfNeeded() { if (view_.get()) return; view_.reset(new AutofillPopupViewGtk(web_contents_, + this, tab_native_view_)); GtkWidget* toplevel = gtk_widget_get_toplevel(tab_native_view_); diff --git a/chrome/browser/autofill/autofill_external_delegate_gtk.h b/chrome/browser/autofill/autofill_external_delegate_gtk.h index 31e2fd0..c2f9185 100644 --- a/chrome/browser/autofill/autofill_external_delegate_gtk.h +++ b/chrome/browser/autofill/autofill_external_delegate_gtk.h @@ -26,11 +26,9 @@ class AutofillExternalDelegateGtk : public AutofillExternalDelegate { virtual ~AutofillExternalDelegateGtk(); - // AutofillExternalDelegate implementation. - virtual void HideAutofillPopup() OVERRIDE; - protected: // AutofillExternalDelegate implementations. + virtual void HideAutofillPopupInternal() OVERRIDE; virtual void OnQueryPlatformSpecific( int query_id, const webkit::forms::FormData& form, diff --git a/chrome/browser/autofill/autofill_external_delegate_unittest.cc b/chrome/browser/autofill/autofill_external_delegate_unittest.cc index f9e6f6c..e0ba283 100644 --- a/chrome/browser/autofill/autofill_external_delegate_unittest.cc +++ b/chrome/browser/autofill/autofill_external_delegate_unittest.cc @@ -7,8 +7,8 @@ #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/string16.h" -#include "chrome/browser/autofill/autofill_external_delegate.h" #include "chrome/browser/autofill/autofill_manager.h" +#include "chrome/browser/autofill/test_autofill_external_delegate.h" #include "chrome/browser/ui/tab_contents/test_tab_contents_wrapper.h" #include "chrome/test/base/testing_profile.h" #include "content/test/test_browser_thread.h" @@ -25,14 +25,12 @@ using webkit::forms::FormField; namespace { -class MockAutofillExternalDelegate : public AutofillExternalDelegate { +class MockAutofillExternalDelegate : public TestAutofillExternalDelegate { public: - explicit MockAutofillExternalDelegate(TabContentsWrapper* wrapper, - AutofillManager* autofill_manager) - : AutofillExternalDelegate(wrapper, autofill_manager) {} - virtual ~MockAutofillExternalDelegate() {} - - virtual void HideAutofillPopup() OVERRIDE {} + MockAutofillExternalDelegate(TabContentsWrapper* wrapper, + AutofillManager* autofill_manger) + : TestAutofillExternalDelegate(wrapper, autofill_manger) {} + ~MockAutofillExternalDelegate() {} MOCK_METHOD5(ApplyAutofillSuggestions, void( const std::vector<string16>& autofill_values, @@ -47,36 +45,53 @@ class MockAutofillExternalDelegate : public AutofillExternalDelegate { const webkit::forms::FormField& field, const gfx::Rect& bounds)); + MOCK_METHOD0(HideAutofillPopup, void()); + private: - DISALLOW_COPY_AND_ASSIGN(MockAutofillExternalDelegate); + virtual void HideAutofillPopupInternal() {}; +}; + +class MockAutofillManager : public AutofillManager { + public: + explicit MockAutofillManager(TabContentsWrapper* tab_contents) + : AutofillManager(tab_contents) {} + ~MockAutofillManager() {} + + MOCK_METHOD4(OnFillAutofillFormData, + void(int query_id, + const webkit::forms::FormData& form, + const webkit::forms::FormField& field, + int unique_id)); }; } // namespace -class AutofillExternalDelegateTest : public TabContentsWrapperTestHarness { +class AutofillExternalDelegateUnitTest : public TabContentsWrapperTestHarness { public: - AutofillExternalDelegateTest() + AutofillExternalDelegateUnitTest() : ui_thread_(BrowserThread::UI, &message_loop_) {} - virtual ~AutofillExternalDelegateTest() {} + virtual ~AutofillExternalDelegateUnitTest() {} virtual void SetUp() OVERRIDE { TabContentsWrapperTestHarness::SetUp(); - autofill_manager_ = new AutofillManager(contents_wrapper()); + autofill_manager_ = new MockAutofillManager(contents_wrapper()); + external_delegate_.reset(new MockAutofillExternalDelegate( + contents_wrapper(), + autofill_manager_)); } protected: - scoped_refptr<AutofillManager> autofill_manager_; + scoped_refptr<MockAutofillManager> autofill_manager_; + scoped_ptr<MockAutofillExternalDelegate> external_delegate_; private: content::TestBrowserThread ui_thread_; - DISALLOW_COPY_AND_ASSIGN(AutofillExternalDelegateTest); + DISALLOW_COPY_AND_ASSIGN(AutofillExternalDelegateUnitTest); }; // Test that our external delegate called the virtual methods at the right time. -TEST_F(AutofillExternalDelegateTest, TestExternalDelegateVirtualCalls) { - MockAutofillExternalDelegate external_delegate(contents_wrapper(), - autofill_manager_); +TEST_F(AutofillExternalDelegateUnitTest, TestExternalDelegateVirtualCalls) { const int kQueryId = 5; const FormData form; FormField field; @@ -84,23 +99,42 @@ TEST_F(AutofillExternalDelegateTest, TestExternalDelegateVirtualCalls) { field.should_autocomplete = true; const gfx::Rect bounds; - EXPECT_CALL(external_delegate, + EXPECT_CALL(*external_delegate_, OnQueryPlatformSpecific(kQueryId, form, field, bounds)); // This should call OnQueryPlatform specific. - external_delegate.OnQuery(kQueryId, form, field, bounds, false); - + external_delegate_->OnQuery(kQueryId, form, field, bounds, false); - EXPECT_CALL(external_delegate, ApplyAutofillSuggestions(_, _, _, _, _)); + EXPECT_CALL(*external_delegate_, ApplyAutofillSuggestions(_, _, _, _, _)); // This should call ApplyAutofillSuggestions. std::vector<string16> autofill_item; autofill_item.push_back(string16()); std::vector<int> autofill_ids; autofill_ids.push_back(1); - external_delegate.OnSuggestionsReturned(kQueryId, - autofill_item, - autofill_item, - autofill_item, - autofill_ids); + external_delegate_->OnSuggestionsReturned(kQueryId, + autofill_item, + autofill_item, + autofill_item, + autofill_ids); + + + EXPECT_CALL(*external_delegate_, HideAutofillPopup()); + + // This should trigger a call to hide the popup since + // we've selected an option. + external_delegate_->DidAcceptAutofillSuggestions(autofill_item[0], + autofill_ids[0], 0); +} + +// Test that the Autofill delegate doesn't try and fill a form with a +// negative unique id. +TEST_F(AutofillExternalDelegateUnitTest, ExternalDelegateInvalidUniqueId) { + // Ensure it doesn't try to preview the negative id. + EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _)).Times(0); + external_delegate_->SelectAutofillSuggestionAtIndex(-1, 0); + + // Ensure it doesn't try to fill the form in with the negative id. + EXPECT_CALL(*autofill_manager_, OnFillAutofillFormData(_, _, _, _)).Times(0); + external_delegate_->DidAcceptAutofillSuggestions(string16(), -1, 0); } diff --git a/chrome/browser/autofill/autofill_manager.h b/chrome/browser/autofill/autofill_manager.h index 9079d09..058ee25 100644 --- a/chrome/browser/autofill/autofill_manager.h +++ b/chrome/browser/autofill/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. @@ -71,12 +71,14 @@ class AutofillManager : public content::WebContentsObserver, } // Called from our external delegate so they cannot be private. - void OnFillAutofillFormData(int query_id, - const webkit::forms::FormData& form, - const webkit::forms::FormField& field, - int unique_id); + virtual void OnFillAutofillFormData(int query_id, + const webkit::forms::FormData& form, + const webkit::forms::FormField& field, + int unique_id); void OnDidShowAutofillSuggestions(bool is_new_popup); void OnDidFillAutofillFormData(const base::TimeTicks& timestamp); + void OnShowAutofillDialog(); + void OnDidPreviewAutofillFormData(); protected: // Only test code should subclass AutofillManager. @@ -158,8 +160,6 @@ class AutofillManager : public content::WebContentsObserver, const webkit::forms::FormField& field, const gfx::Rect& bounding_box, bool display_warning); - void OnShowAutofillDialog(); - void OnDidPreviewAutofillFormData(); void OnDidEndTextFieldEditing(); void OnHideAutofillPopup(); diff --git a/chrome/browser/autofill/autofill_manager_unittest.cc b/chrome/browser/autofill/autofill_manager_unittest.cc index ee42ca3..3d1e400 100644 --- a/chrome/browser/autofill/autofill_manager_unittest.cc +++ b/chrome/browser/autofill/autofill_manager_unittest.cc @@ -15,12 +15,12 @@ #include "base/utf_string_conversions.h" #include "chrome/browser/autocomplete_history_manager.h" #include "chrome/browser/autofill/autofill_common_test.h" -#include "chrome/browser/autofill/autofill_external_delegate.h" #include "chrome/browser/autofill/autofill_manager.h" #include "chrome/browser/autofill/autofill_profile.h" #include "chrome/browser/autofill/credit_card.h" #include "chrome/browser/autofill/personal_data_manager.h" #include "chrome/browser/autofill/personal_data_manager_factory.h" +#include "chrome/browser/autofill/test_autofill_external_delegate.h" #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" @@ -2862,11 +2862,11 @@ TEST_F(AutofillManagerTest, DeterminePossibleFieldTypesForUpload) { namespace { -class MockAutofillExternalDelegate : public AutofillExternalDelegate { +class MockAutofillExternalDelegate : public TestAutofillExternalDelegate { public: explicit MockAutofillExternalDelegate(TabContentsWrapper* wrapper, AutofillManager* autofill_manager) - : AutofillExternalDelegate(wrapper, autofill_manager) {} + : TestAutofillExternalDelegate(wrapper, autofill_manager) {} virtual ~MockAutofillExternalDelegate() {} MOCK_METHOD5(OnQuery, void(int query_id, @@ -2875,15 +2875,6 @@ class MockAutofillExternalDelegate : public AutofillExternalDelegate { const gfx::Rect& bounds, bool display_warning)); - virtual void HideAutofillPopup() OVERRIDE {} - - virtual void ApplyAutofillSuggestions( - const std::vector<string16>& autofill_values, - const std::vector<string16>& autofill_labels, - const std::vector<string16>& autofill_icons, - const std::vector<int>& autofill_unique_ids, - int separator_index) OVERRIDE {} - virtual void OnQueryPlatformSpecific(int query_id, const webkit::forms::FormData& form, const webkit::forms::FormField& field, diff --git a/chrome/browser/autofill/autofill_popup_view.cc b/chrome/browser/autofill/autofill_popup_view.cc index bd28553..7e3ba5a 100644 --- a/chrome/browser/autofill/autofill_popup_view.cc +++ b/chrome/browser/autofill/autofill_popup_view.cc @@ -4,13 +4,19 @@ #include "chrome/browser/autofill/autofill_popup_view.h" +#include "base/logging.h" +#include "chrome/browser/autofill/autofill_external_delegate.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/notification_types.h" -AutofillPopupView::AutofillPopupView(content::WebContents* web_contents) { +AutofillPopupView::AutofillPopupView( + content::WebContents* web_contents, + AutofillExternalDelegate* external_delegate) + : external_delegate_(external_delegate), + selected_line_(-1) { registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_HIDDEN, content::Source<content::WebContents>(web_contents)); @@ -23,6 +29,12 @@ AutofillPopupView::AutofillPopupView(content::WebContents* web_contents) { AutofillPopupView::~AutofillPopupView() {} +void AutofillPopupView::Hide() { + HideInternal(); + + external_delegate_->ClearPreviewedForm(); +} + void AutofillPopupView::Show(const std::vector<string16>& autofill_values, const std::vector<string16>& autofill_labels, const std::vector<string16>& autofill_icons, @@ -38,6 +50,25 @@ void AutofillPopupView::Show(const std::vector<string16>& autofill_values, ShowInternal(); } +void AutofillPopupView::SetSelectedLine(int selected_line) { + if (selected_line_ == selected_line) + return; + + if (selected_line_ != -1) + InvalidateRow(selected_line_); + + if (selected_line != -1) + InvalidateRow(selected_line); + + selected_line_ = selected_line; + + if (selected_line_ != -1) { + external_delegate_->SelectAutofillSuggestionAtIndex( + autofill_unique_ids_[selected_line_], + selected_line); + } +} + void AutofillPopupView::Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) { diff --git a/chrome/browser/autofill/autofill_popup_view.h b/chrome/browser/autofill/autofill_popup_view.h index 9344ee0..1ca8908 100644 --- a/chrome/browser/autofill/autofill_popup_view.h +++ b/chrome/browser/autofill/autofill_popup_view.h @@ -18,13 +18,16 @@ namespace content { class WebContents; } +class AutofillExternalDelegate; + class AutofillPopupView : public content::NotificationObserver { public: - explicit AutofillPopupView(content::WebContents* web_contents); + explicit AutofillPopupView(content::WebContents* web_contents, + AutofillExternalDelegate* external_delegate_); virtual ~AutofillPopupView(); - // Hide the popup from view. - virtual void Hide() = 0; + // Hide the popup from view. Platform-indepent work. + virtual void Hide(); // Display the autofill popup and fill it in with the values passed in. // Platform-independent work. @@ -34,7 +37,6 @@ class AutofillPopupView : public content::NotificationObserver { const std::vector<int>& autofill_unique_ids, int separator_index); - void set_element_bounds(const gfx::Rect& bounds) { element_bounds_ = bounds; } @@ -46,6 +48,14 @@ class AutofillPopupView : public content::NotificationObserver { // Platform-dependent work. virtual void ShowInternal() = 0; + // Hide the popup from view. Platform-dependent work. + virtual void HideInternal() = 0; + + // Invalide the given row and redraw it. + virtual void InvalidateRow(size_t row) = 0; + + AutofillExternalDelegate* external_delegate() { return external_delegate_; } + const std::vector<string16>& autofill_values() const { return autofill_values_; } @@ -55,8 +65,16 @@ class AutofillPopupView : public content::NotificationObserver { const std::vector<string16>& autofill_icons() const { return autofill_icons_; } + const std::vector<int>& autofill_unique_ids() const { + return autofill_unique_ids_; + } int separator_index() const { return separator_index_; } + int selected_line() const { return selected_line_; } + + // Change which line is currently selected by the user. + void SetSelectedLine(int selected_line); + private: // content::NotificationObserver method override. virtual void Observe(int type, @@ -66,6 +84,8 @@ class AutofillPopupView : public content::NotificationObserver { // A scoped container for notification registries. content::NotificationRegistrar registrar_; + AutofillExternalDelegate* external_delegate_; + // The bounds of the text element that is the focus of the Autofill. gfx::Rect element_bounds_; @@ -78,6 +98,10 @@ class AutofillPopupView : public content::NotificationObserver { // The location of the separator index (which separates the returned values // from the Autofill options). int separator_index_; + + // The line that is currently selected by the user. + // -1 indicates that no line is currently selected. + int selected_line_; }; #endif // CHROME_BROWSER_AUTOFILL_AUTOFILL_POPUP_VIEW_H_ diff --git a/chrome/browser/autofill/autofill_popup_view_browsertest.cc b/chrome/browser/autofill/autofill_popup_view_browsertest.cc index 06b80ad..1990a97 100644 --- a/chrome/browser/autofill/autofill_popup_view_browsertest.cc +++ b/chrome/browser/autofill/autofill_popup_view_browsertest.cc @@ -5,6 +5,7 @@ #include "chrome/browser/autofill/autofill_popup_view.h" #include "base/memory/scoped_ptr.h" +#include "chrome/browser/autofill/test_autofill_external_delegate.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/navigation_controller.h" @@ -18,38 +19,70 @@ #include "testing/gtest/include/gtest/gtest.h" using ::testing::AtLeast; +using testing::_; + +namespace { + +class MockAutofillExternalDelegate : public TestAutofillExternalDelegate { + public: + MockAutofillExternalDelegate() : TestAutofillExternalDelegate(NULL, NULL) {} + ~MockAutofillExternalDelegate() {} + + virtual void SelectAutofillSuggestionAtIndex(int unique_id, int list_index) + OVERRIDE {} +}; class TestAutofillPopupView : public AutofillPopupView { public: - explicit TestAutofillPopupView(content::WebContents* web_contents) - : AutofillPopupView(web_contents) {} + explicit TestAutofillPopupView( + content::WebContents* web_contents, + AutofillExternalDelegate* autofill_external_delegate) + : AutofillPopupView(web_contents, autofill_external_delegate) {} virtual ~TestAutofillPopupView() {} MOCK_METHOD0(Hide, void()); + MOCK_METHOD1(InvalidateRow, void(size_t)); + + void SetSelectedLine(size_t selected_line) { + AutofillPopupView::SetSelectedLine(selected_line); + } + + protected: virtual void ShowInternal() OVERRIDE {} + + virtual void HideInternal() OVERRIDE {} }; +} // namespace + class AutofillPopupViewBrowserTest : public InProcessBrowserTest { public: AutofillPopupViewBrowserTest() {} virtual ~AutofillPopupViewBrowserTest() {} + virtual void SetUpOnMainThread() OVERRIDE { + web_contents_ = browser()->GetSelectedWebContents(); + ASSERT_TRUE(web_contents_ != NULL); + + autofill_popup_view_.reset(new TestAutofillPopupView( + web_contents_, + &autofill_external_delegate_)); + } + protected: + content::WebContents* web_contents_; scoped_ptr<TestAutofillPopupView> autofill_popup_view_; + MockAutofillExternalDelegate autofill_external_delegate_; }; -IN_PROC_BROWSER_TEST_F(InProcessBrowserTest, SwitchTabAndHideAutofillPopup) { - content::WebContents* web_contents = browser()->GetSelectedWebContents(); - TestAutofillPopupView autofill_popup_view(web_contents); - - // Using AtLeast here because current Hide is called once on Linux and Mac, - // and three times on Windows and ChromeOS. http://crbug.com/109269 - EXPECT_CALL(autofill_popup_view, Hide()).Times(AtLeast(1)); +IN_PROC_BROWSER_TEST_F(AutofillPopupViewBrowserTest, + SwitchTabAndHideAutofillPopup) { + EXPECT_CALL(*autofill_popup_view_, Hide()).Times(AtLeast(1)); ui_test_utils::WindowedNotificationObserver observer( content::NOTIFICATION_WEB_CONTENTS_HIDDEN, - content::Source<content::WebContents>(web_contents)); + content::Source<content::WebContents>(web_contents_)); browser()->AddSelectedTabWithURL(GURL(chrome::kAboutBlankURL), content::PAGE_TRANSITION_START_PAGE); observer.Wait(); @@ -57,16 +90,14 @@ IN_PROC_BROWSER_TEST_F(InProcessBrowserTest, SwitchTabAndHideAutofillPopup) { // The mock verifies that the call was made. } -IN_PROC_BROWSER_TEST_F(InProcessBrowserTest, +IN_PROC_BROWSER_TEST_F(AutofillPopupViewBrowserTest, TestPageNavigationHidingAutofillPopup) { - content::WebContents* web_contents = browser()->GetSelectedWebContents(); - TestAutofillPopupView autofill_popup_view(web_contents); - EXPECT_CALL(autofill_popup_view, Hide()); + EXPECT_CALL(*autofill_popup_view_, Hide()).Times(AtLeast(1)); ui_test_utils::WindowedNotificationObserver observer( content::NOTIFICATION_NAV_ENTRY_COMMITTED, content::Source<content::NavigationController>( - &(web_contents->GetController()))); + &(web_contents_->GetController()))); browser()->OpenURL(content::OpenURLParams( GURL(chrome::kAboutBlankURL), content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false)); @@ -77,3 +108,27 @@ IN_PROC_BROWSER_TEST_F(InProcessBrowserTest, // The mock verifies that the call was made. } + +IN_PROC_BROWSER_TEST_F(AutofillPopupViewBrowserTest, + SetSelectedAutofillLineAndCallInvalidate) { + std::vector<string16> autofill_values; + autofill_values.push_back(string16()); + std::vector<int> autofill_ids; + autofill_ids.push_back(0); + autofill_popup_view_->Show( + autofill_values, autofill_values, autofill_values, autofill_ids, 0); + + // Make sure that when a new line is selected, it is invalidated so it can + // be updated to show it is selected. + int selected_line = 0; + EXPECT_CALL(*autofill_popup_view_, InvalidateRow(selected_line)); + autofill_popup_view_->SetSelectedLine(selected_line); + + // Ensure that the row isn't invalidated if it didn't change. + EXPECT_CALL(*autofill_popup_view_, InvalidateRow(selected_line)).Times(0); + autofill_popup_view_->SetSelectedLine(selected_line); + + // Change back to no selection. + EXPECT_CALL(*autofill_popup_view_, InvalidateRow(selected_line)); + autofill_popup_view_->SetSelectedLine(-1); +} diff --git a/chrome/browser/autofill/test_autofill_external_delegate.cc b/chrome/browser/autofill/test_autofill_external_delegate.cc new file mode 100644 index 0000000..9f076f1 --- /dev/null +++ b/chrome/browser/autofill/test_autofill_external_delegate.cc @@ -0,0 +1,26 @@ +// 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/test_autofill_external_delegate.h" + +TestAutofillExternalDelegate::TestAutofillExternalDelegate( + TabContentsWrapper* wrapper, AutofillManager* autofill_manager) : + AutofillExternalDelegate(wrapper, autofill_manager) {} + +TestAutofillExternalDelegate::~TestAutofillExternalDelegate() {} + +void TestAutofillExternalDelegate::ApplyAutofillSuggestions( + const std::vector<string16>& autofill_values, + const std::vector<string16>& autofill_labels, + const std::vector<string16>& autofill_icons, + const std::vector<int>& autofill_unique_ids, + int separator_index) {} + +void TestAutofillExternalDelegate::OnQueryPlatformSpecific( + int query_id, + const webkit::forms::FormData& form, + const webkit::forms::FormField& field, + const gfx::Rect& bounds) {} + +void TestAutofillExternalDelegate::HideAutofillPopupInternal() {} diff --git a/chrome/browser/autofill/test_autofill_external_delegate.h b/chrome/browser/autofill/test_autofill_external_delegate.h new file mode 100644 index 0000000..771fcf8 --- /dev/null +++ b/chrome/browser/autofill/test_autofill_external_delegate.h @@ -0,0 +1,42 @@ +// 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_TEST_AUTOFILL_EXTERNAL_DELEGATE_H_ +#define CHROME_BROWSER_AUTOFILL_TEST_AUTOFILL_EXTERNAL_DELEGATE_H_ +#pragma once + +#include "chrome/browser/autofill/autofill_external_delegate.h" + +class AutofillManager; +class TabContentsWrapper; + +// This test class is meant to give tests a base AutofillExternalDelegate +// class that requires no additional work to compile with (i.e. all the +// pure virtual functions have been giving empty methods). +class TestAutofillExternalDelegate : public AutofillExternalDelegate { + public: + TestAutofillExternalDelegate(TabContentsWrapper* wrapper, + AutofillManager* autofill_manager); + virtual ~TestAutofillExternalDelegate(); + + virtual void ApplyAutofillSuggestions( + const std::vector<string16>& autofill_values, + const std::vector<string16>& autofill_labels, + const std::vector<string16>& autofill_icons, + const std::vector<int>& autofill_unique_ids, + int separator_index) OVERRIDE; + + virtual void OnQueryPlatformSpecific(int query_id, + const webkit::forms::FormData& form, + const webkit::forms::FormField& field, + const gfx::Rect& bounds) OVERRIDE; + + + virtual void HideAutofillPopupInternal() OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(TestAutofillExternalDelegate); +}; + +#endif // CHROME_BROWSER_AUTOFILL_TEST_AUTOFILL_EXTERNAL_DELEGATE_H_ diff --git a/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.cc b/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.cc index 93a347e..c65d110 100644 --- a/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.cc +++ b/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/utf_string_conversions.h" +#include "chrome/browser/autofill/autofill_external_delegate.h" #include "chrome/browser/ui/gtk/gtk_util.h" #include "ui/base/gtk/gtk_compat.h" #include "ui/base/gtk/gtk_hig_constants.h" @@ -15,6 +16,7 @@ namespace { const GdkColor kBorderColor = GDK_COLOR_RGB(0xc7, 0xca, 0xce); +const GdkColor kHoveredBackgroundColor = GDK_COLOR_RGB(0x0CD, 0xCD, 0xCD); const GdkColor kTextColor = GDK_COLOR_RGB(0x00, 0x00, 0x00); // The amount of minimum padding between the Autofill value and label in pixels. @@ -35,9 +37,11 @@ gfx::Rect GetRectForRow(size_t index, int width, int height) { } // namespace -AutofillPopupViewGtk::AutofillPopupViewGtk(content::WebContents* web_contents, - GtkWidget* parent) - : AutofillPopupView(web_contents), +AutofillPopupViewGtk::AutofillPopupViewGtk( + content::WebContents* web_contents, + AutofillExternalDelegate* external_delegate, + GtkWidget* parent) + : AutofillPopupView(web_contents, external_delegate), parent_(parent), window_(gtk_window_new(GTK_WINDOW_POPUP)) { CHECK(parent != NULL); @@ -45,11 +49,19 @@ AutofillPopupViewGtk::AutofillPopupViewGtk(content::WebContents* web_contents, gtk_widget_set_app_paintable(window_, TRUE); gtk_widget_set_double_buffered(window_, TRUE); - // Setup the window to ensure it recieves the expose event. - gtk_widget_add_events(window_, GDK_EXPOSURE_MASK); + // Setup the window to ensure it receives the expose event. + gtk_widget_add_events(window_, GDK_BUTTON_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_EXPOSURE_MASK | + GDK_POINTER_MOTION_MASK); g_signal_connect(window_, "expose-event", G_CALLBACK(HandleExposeThunk), this); + g_signal_connect(window_, "motion-notify-event", + G_CALLBACK(HandleMotionThunk), this); + g_signal_connect(window_, "button-release-event", + G_CALLBACK(HandleButtonReleaseThunk), this); + // Cache the layout so we don't have to create it for every expose. layout_ = gtk_widget_create_pango_layout(window_, NULL); @@ -61,19 +73,7 @@ AutofillPopupViewGtk::~AutofillPopupViewGtk() { gtk_widget_destroy(window_); } -void AutofillPopupViewGtk::Hide() { - gtk_widget_hide(window_); -} - void AutofillPopupViewGtk::ShowInternal() { - gint origin_x, origin_y; - gdk_window_get_origin(gtk_widget_get_window(parent_), &origin_x, &origin_y); - - // Move the popup to appear right below the text field it is using. - gtk_window_move(GTK_WINDOW(window_), - origin_x + element_bounds().x(), - origin_y + element_bounds().y() + element_bounds().height()); - // Find out the maximum bounds required by the popup. // TODO(csharp): Once the icon is also displayed it will affect the required // size so it will need to be included in the calculation. @@ -86,6 +86,18 @@ void AutofillPopupViewGtk::ShowInternal() { font_.GetStringWidth(autofill_labels()[i])); } + gint origin_x, origin_y; + gdk_window_get_origin(gtk_widget_get_window(parent_), &origin_x, &origin_y); + + // Move the popup to appear right below the text field it is using. + bounds_.SetRect( + origin_x + element_bounds().x(), + origin_y + element_bounds().y() + element_bounds().height(), + popup_width, + row_height_ * autofill_values().size()); + + gtk_window_move(GTK_WINDOW(window_), bounds_.x(), bounds_.y()); + gtk_widget_set_size_request( window_, popup_width, @@ -98,6 +110,33 @@ void AutofillPopupViewGtk::ShowInternal() { ui::StackPopupWindow(window_, toplevel); } +void AutofillPopupViewGtk::HideInternal() { + gtk_widget_hide(window_); +} + +void AutofillPopupViewGtk::InvalidateRow(size_t row) { + GdkRectangle row_rect = GetRectForRow( + row, bounds_.width(), row_height_).ToGdkRectangle(); + gdk_window_invalidate_rect(window_->window, &row_rect, FALSE); +} + +gboolean AutofillPopupViewGtk::HandleButtonRelease(GtkWidget* widget, + GdkEventButton* event) { + // We only care about the left click. + if (event->button != 1) + return FALSE; + + size_t line = LineFromY(event->y); + DCHECK_LT(line, autofill_values().size()); + + external_delegate()->DidAcceptAutofillSuggestions( + autofill_values()[line], + autofill_unique_ids()[line], + line); + + return TRUE; +} + gboolean AutofillPopupViewGtk::HandleExpose(GtkWidget* widget, GdkEventExpose* event) { gfx::Rect window_rect = GetWindowRect(event->window); @@ -139,6 +178,13 @@ gboolean AutofillPopupViewGtk::HandleExpose(GtkWidget* widget, cairo_restore(cr); } + if (selected_line() == static_cast<int>(i)) { + gdk_cairo_set_source_color(cr, &kHoveredBackgroundColor); + cairo_rectangle(cr, line_rect.x(), line_rect.y(), + line_rect.width(), line_rect.height()); + cairo_fill(cr); + } + // Center the text within the line. int content_y = std::max( line_rect.y(), @@ -168,6 +214,15 @@ gboolean AutofillPopupViewGtk::HandleExpose(GtkWidget* widget, return TRUE; } +gboolean AutofillPopupViewGtk::HandleMotion(GtkWidget* widget, + GdkEventMotion* event) { + int line = LineFromY(event->y); + + SetSelectedLine(line); + + return TRUE; +} + void AutofillPopupViewGtk::SetupLayout(const gfx::Rect& window_rect, const GdkColor& text_color) { int allocated_content_width = window_rect.width(); @@ -185,3 +240,7 @@ void AutofillPopupViewGtk::SetupLayout(const gfx::Rect& window_rect, pango_layout_set_attributes(layout_, attrs); // Ref taken. pango_attr_list_unref(attrs); } + +int AutofillPopupViewGtk::LineFromY(int y) { + return y / row_height_; +} diff --git a/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.h b/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.h index 8e86987..999dd46 100644 --- a/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.h +++ b/chrome/browser/ui/gtk/autofill/autofill_popup_view_gtk.h @@ -17,26 +17,39 @@ namespace gfx { class Rect; } +typedef struct _GdkEventButton GdkEventButton; typedef struct _GdkEventExpose GdkEventExpose; +typedef struct _GdkEventMotion GdkEventMotion; typedef struct _GdkColor GdkColor; typedef struct _GtkWidget GtkWidget; class AutofillPopupViewGtk : public AutofillPopupView { public: - AutofillPopupViewGtk(content::WebContents* web_contents, GtkWidget* parent); + AutofillPopupViewGtk(content::WebContents* web_contents, + AutofillExternalDelegate* external_delegate, + GtkWidget* parent); virtual ~AutofillPopupViewGtk(); + protected: // AutofillPopupView implementations. - virtual void Hide() OVERRIDE; virtual void ShowInternal() OVERRIDE; + virtual void HideInternal() OVERRIDE; + virtual void InvalidateRow(size_t row) OVERRIDE; private: + CHROMEGTK_CALLBACK_1(AutofillPopupViewGtk, gboolean, HandleButtonRelease, + GdkEventButton*); CHROMEGTK_CALLBACK_1(AutofillPopupViewGtk, gboolean, HandleExpose, GdkEventExpose*); + CHROMEGTK_CALLBACK_1(AutofillPopupViewGtk, gboolean, HandleMotion, + GdkEventMotion*); // Setup the pango layout to display the autofill results. void SetupLayout(const gfx::Rect& window_rect, const GdkColor& text_color); + // Convert a y-coordinate to the closest line. + int LineFromY(int y); + GtkWidget* parent_; // Weak reference. GtkWidget* window_; // Strong reference. PangoLayout* layout_; // Strong reference @@ -44,6 +57,9 @@ class AutofillPopupViewGtk : public AutofillPopupView { // The height of each individual Autofill popup row. int row_height_; + + // The size of the popup. + gfx::Rect bounds_; }; #endif // CHROME_BROWSER_UI_GTK_AUTOFILL_AUTOFILL_POPUP_VIEW_GTK_H_ diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 7cddca8..2129fb2 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -83,6 +83,8 @@ 'browser/autofill/autofill_common_test.h', 'browser/autofill/data_driven_test.cc', 'browser/autofill/data_driven_test.h', + 'browser/autofill/test_autofill_external_delegate.cc', + 'browser/autofill/test_autofill_external_delegate.h', 'browser/automation/mock_tab_event_observer.cc', 'browser/automation/mock_tab_event_observer.h', 'browser/chromeos/cros/mock_cryptohome_library.cc', diff --git a/chrome/common/autofill_messages.h b/chrome/common/autofill_messages.h index 95adf65..d7a4b05 100644 --- a/chrome/common/autofill_messages.h +++ b/chrome/common/autofill_messages.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. @@ -95,6 +95,22 @@ IPC_MESSAGE_ROUTED1( IPC_MESSAGE_ROUTED1(AutofillMsg_SelectAutofillSuggestionAtIndex, int /* listIndex */) +// Tells the renderer that the next form will be filled for real. +IPC_MESSAGE_ROUTED0(AutofillMsg_SetAutofillActionFill) + +// Clears the currently displayed Autofill results. +IPC_MESSAGE_ROUTED0(AutofillMsg_ClearForm) + +// Tells the renderer that the next form will be filled as a preview. +IPC_MESSAGE_ROUTED0(AutofillMsg_SetAutofillActionPreview) + +// Tells the renderer that the Autofill previewed form should be cleared. +IPC_MESSAGE_ROUTED0(AutofillMsg_ClearPreviewedForm) + +// Sets the currently selected nodes value. +IPC_MESSAGE_ROUTED1(AutofillMsg_SetNodeText, + string16) + // Autofill messages sent from the renderer to the browser. // Notification that forms have been seen that are candidates for diff --git a/chrome/renderer/autofill/autofill_agent.cc b/chrome/renderer/autofill/autofill_agent.cc index 9130b10..54731fe9 100644 --- a/chrome/renderer/autofill/autofill_agent.cc +++ b/chrome/renderer/autofill/autofill_agent.cc @@ -76,6 +76,16 @@ bool AutofillAgent::OnMessageReceived(const IPC::Message& message) { OnFieldTypePredictionsAvailable) IPC_MESSAGE_HANDLER(AutofillMsg_SelectAutofillSuggestionAtIndex, OnSelectAutofillSuggestionAtIndex) + IPC_MESSAGE_HANDLER(AutofillMsg_SetAutofillActionFill, + OnSetAutofillActionFill) + IPC_MESSAGE_HANDLER(AutofillMsg_ClearForm, + OnClearForm) + IPC_MESSAGE_HANDLER(AutofillMsg_SetAutofillActionPreview, + OnSetAutofillActionPreview) + IPC_MESSAGE_HANDLER(AutofillMsg_ClearPreviewedForm, + OnClearPreviewedForm) + IPC_MESSAGE_HANDLER(AutofillMsg_SetNodeText, + OnSetNodeText) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -139,6 +149,8 @@ void AutofillAgent::didAcceptAutofillSuggestion(const WebNode& node, if (password_autofill_manager_->DidAcceptAutofillSuggestion(node, value)) return; + DCHECK(node == autofill_query_element_); + if (suggestions_options_index_ != -1 && index == static_cast<unsigned>(suggestions_options_index_)) { // User selected 'Autofill Options'. @@ -146,15 +158,11 @@ void AutofillAgent::didAcceptAutofillSuggestion(const WebNode& node, } else if (suggestions_clear_index_ != -1 && index == static_cast<unsigned>(suggestions_clear_index_)) { // User selected 'Clear form'. - DCHECK(node == autofill_query_element_); form_cache_.ClearFormWithElement(autofill_query_element_); } else if (!unique_id) { // User selected an Autocomplete entry, so we fill directly. WebInputElement element = node.toConst<WebInputElement>(); - - string16 substring = value; - substring = substring.substr(0, element.maxLength()); - element.setValue(substring, true); + SetNodeText(value, &element); } else { // Fill the values for the whole form. FillAutofillFormData(node, unique_id, AUTOFILL_FILL); @@ -337,6 +345,8 @@ void AutofillAgent::OnFormDataFilled(int query_id, if (!render_view()->GetWebView() || query_id != autofill_query_id_) return; + was_query_node_autofilled_ = autofill_query_element_.isAutofilled(); + switch (autofill_action_) { case AUTOFILL_FILL: FillForm(form, autofill_query_element_); @@ -344,6 +354,8 @@ void AutofillAgent::OnFormDataFilled(int query_id, base::TimeTicks::Now())); break; case AUTOFILL_PREVIEW: + didClearAutofillSelection(autofill_query_element_); + PreviewForm(form, autofill_query_element_); Send(new AutofillHostMsg_DidPreviewAutofillFormData(routing_id())); break; @@ -366,6 +378,26 @@ void AutofillAgent::OnSelectAutofillSuggestionAtIndex(int listIndex) { // render_view()->webview()->selectAutofillSuggestionAtIndex(listIndex); } +void AutofillAgent::OnSetAutofillActionFill() { + autofill_action_ = AUTOFILL_FILL; +} + +void AutofillAgent::OnClearForm() { + form_cache_.ClearFormWithElement(autofill_query_element_); +} + +void AutofillAgent::OnSetAutofillActionPreview() { + autofill_action_ = AUTOFILL_PREVIEW; +} + +void AutofillAgent::OnClearPreviewedForm() { + didClearAutofillSelection(autofill_query_element_); +} + +void AutofillAgent::OnSetNodeText(const string16& value) { + SetNodeText(value, &autofill_query_element_); +} + void AutofillAgent::ShowSuggestions(const WebInputElement& element, bool autofill_on_empty_values, bool requires_caret_at_end, @@ -442,9 +474,16 @@ void AutofillAgent::FillAutofillFormData(const WebNode& node, } autofill_action_ = action; - was_query_node_autofilled_ = field.is_autofilled; Send(new AutofillHostMsg_FillAutofillFormData( routing_id(), autofill_query_id_, form, field, unique_id)); } +void AutofillAgent::SetNodeText(const string16& value, + WebKit::WebInputElement* node) { + string16 substring = value; + substring = substring.substr(0, node->maxLength()); + + node->setValue(substring, true); +} + } // namespace autofill diff --git a/chrome/renderer/autofill/autofill_agent.h b/chrome/renderer/autofill/autofill_agent.h index 85ea7e6..d02a903 100644 --- a/chrome/renderer/autofill/autofill_agent.h +++ b/chrome/renderer/autofill/autofill_agent.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. @@ -105,6 +105,11 @@ class AutofillAgent : public content::RenderViewObserver, // For external Autofill selection. void OnSelectAutofillSuggestionAtIndex(int listIndex); + void OnSetAutofillActionFill(); + void OnClearForm(); + void OnSetAutofillActionPreview(); + void OnClearPreviewedForm(); + void OnSetNodeText(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 @@ -146,6 +151,9 @@ class AutofillAgent : public content::RenderViewObserver, webkit::forms::FormData* form, webkit::forms::FormField* field) WARN_UNUSED_RESULT; + // Set |node| to display the given |value|. + void SetNodeText(const string16& value, WebKit::WebInputElement* node); + FormCache form_cache_; PasswordAutofillManager* password_autofill_manager_; // WEAK reference. diff --git a/chrome/renderer/autofill/autofill_browsertest.cc b/chrome/renderer/autofill/autofill_browsertest.cc index 50694dd..34dbb69 100644 --- a/chrome/renderer/autofill/autofill_browsertest.cc +++ b/chrome/renderer/autofill/autofill_browsertest.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. @@ -83,6 +83,9 @@ TEST_F(ChromeRenderViewTest, SendForms) { WebInputElement firstname = document.getElementById("firstname").to<WebInputElement>(); + // Make sure to query for Autofill suggestions before selecting one. + autofill_agent_->QueryAutofillSuggestions(firstname, false); + // Accept suggestion that contains a label. Labeled items indicate Autofill // as opposed to Autocomplete. We're testing this distinction below with // the |AutofillHostMsg_FillAutofillFormData::ID| message. @@ -149,6 +152,9 @@ TEST_F(ChromeRenderViewTest, FillFormElement) { document.getElementById("middlename").to<WebInputElement>(); middlename.setAutofilled(true); + // Make sure to query for Autofill suggestions before selecting one. + autofill_agent_->QueryAutofillSuggestions(firstname, false); + // Accept a suggestion in a form that has been auto-filled. This triggers // the direct filling of the firstname element with value parameter. autofill_agent_->didAcceptAutofillSuggestion(firstname, |