summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgcasto@chromium.org <gcasto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-31 23:12:23 +0000
committergcasto@chromium.org <gcasto@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-01-31 23:12:23 +0000
commit3cbdf9363d3c014d5ff484a30ab36c6bc9972bc9 (patch)
tree4a8d97ff50b587f5e95ea3826cec76f9b79474b9
parent598ed4879c529168b8ca5186eed85ec691ee09ea (diff)
downloadchromium_src-3cbdf9363d3c014d5ff484a30ab36c6bc9972bc9.zip
chromium_src-3cbdf9363d3c014d5ff484a30ab36c6bc9972bc9.tar.gz
chromium_src-3cbdf9363d3c014d5ff484a30ab36c6bc9972bc9.tar.bz2
Re-land r248110 "[Password Generation] Enable new UI"
Replaces key icon and associated bubble with autofill style dropdown. Changes include - Update PasswordGenerationAgentTest - Add interactive_uitest for new UI. - Sync edits to confirmation password field(s). - Update UMA stats to more accurately reflect user choices. Updating the generation popup to match the latest mocks and implement the editing popup will come in a later CL. This reverts r248119 and fixes a use-after-free in PasswordGenerationPopupControllerImpl::PossiblyAcceptPassword() R=asvitkine@chromium.org, estade@chromium.org TBR=kenrb@chromium.org, sky@chromium.org, torne@chromium.org BUG=318977 Review URL: https://codereview.chromium.org/151503006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@248297 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--android_webview/renderer/aw_content_renderer_client.cc2
-rw-r--r--chrome/browser/chrome_content_browser_client.cc2
-rw-r--r--chrome/browser/password_manager/password_generation_interactive_uitest.cc147
-rw-r--r--chrome/browser/password_manager/password_generation_manager.cc65
-rw-r--r--chrome/browser/password_manager/password_generation_manager.h35
-rw-r--r--chrome/browser/ui/autofill/password_generation_popup_controller_impl.cc14
-rw-r--r--chrome/browser/ui/autofill/password_generation_popup_controller_impl.h3
-rw-r--r--chrome/browser/ui/autofill/password_generation_popup_view.cc21
-rw-r--r--chrome/browser/ui/autofill/tab_autofill_manager_delegate.cc7
-rw-r--r--chrome/chrome_browser_ui.gypi1
-rw-r--r--chrome/chrome_tests.gypi5
-rw-r--r--chrome/chrome_tests_unit.gypi2
-rw-r--r--chrome/renderer/autofill/password_generation_agent_browsertest.cc146
-rw-r--r--chrome/renderer/chrome_content_renderer_client.cc9
-rw-r--r--chrome/test/base/chrome_render_view_test.cc12
-rw-r--r--chrome/test/base/chrome_render_view_test.h2
-rw-r--r--components/autofill/content/common/autofill_messages.h12
-rw-r--r--components/autofill/content/renderer/autofill_agent.cc18
-rw-r--r--components/autofill/content/renderer/autofill_agent.h9
-rw-r--r--components/autofill/content/renderer/form_autofill_util.cc8
-rw-r--r--components/autofill/content/renderer/form_autofill_util.h4
-rw-r--r--components/autofill/content/renderer/password_generation_agent.cc169
-rw-r--r--components/autofill/content/renderer/password_generation_agent.h40
-rw-r--r--components/autofill/content/renderer/test_password_generation_agent.cc33
-rw-r--r--components/autofill/content/renderer/test_password_generation_agent.h44
-rw-r--r--components/autofill/core/common/autofill_switches.cc8
-rw-r--r--components/autofill/core/common/autofill_switches.h2
-rw-r--r--components/autofill/core/common/password_generation_util.h29
-rw-r--r--tools/metrics/histograms/histograms.xml26
29 files changed, 703 insertions, 172 deletions
diff --git a/android_webview/renderer/aw_content_renderer_client.cc b/android_webview/renderer/aw_content_renderer_client.cc
index 224e1ed..628d284 100644
--- a/android_webview/renderer/aw_content_renderer_client.cc
+++ b/android_webview/renderer/aw_content_renderer_client.cc
@@ -139,7 +139,7 @@ void AwContentRendererClient::RenderViewCreated(
// autofill agent to store a weakptr).
autofill::PasswordAutofillAgent* password_autofill_agent =
new autofill::PasswordAutofillAgent(render_view);
- new autofill::AutofillAgent(render_view, password_autofill_agent);
+ new autofill::AutofillAgent(render_view, password_autofill_agent, NULL);
}
std::string AwContentRendererClient::GetDefaultEncoding() {
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 1fc665f..4863fbe 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -1545,7 +1545,7 @@ void ChromeContentBrowserClient::AppendExtraCommandLineSwitches(
autofill::switches::kEnableIgnoreAutocompleteOff,
autofill::switches::kEnableInteractiveAutocomplete,
autofill::switches::kEnablePasswordGeneration,
- autofill::switches::kNoAutofillNecessaryForPasswordGeneration,
+ autofill::switches::kLocalHeuristicsOnlyForPasswordGeneration,
extensions::switches::kAllowHTTPBackgroundPage,
extensions::switches::kAllowLegacyExtensionManifests,
extensions::switches::kAllowScriptingGallery,
diff --git a/chrome/browser/password_manager/password_generation_interactive_uitest.cc b/chrome/browser/password_manager/password_generation_interactive_uitest.cc
new file mode 100644
index 0000000..fff3b09
--- /dev/null
+++ b/chrome/browser/password_manager/password_generation_interactive_uitest.cc
@@ -0,0 +1,147 @@
+// Copyright 2014 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/command_line.h"
+#include "chrome/browser/password_manager/password_generation_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/autofill/password_generation_popup_observer.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/common/autofill_switches.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test_utils.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+
+namespace {
+
+class TestPopupObserver : public autofill::PasswordGenerationPopupObserver {
+ public:
+ TestPopupObserver()
+ : popup_showing_(false) {}
+ virtual ~TestPopupObserver() {}
+
+ virtual void OnPopupShown() OVERRIDE {
+ popup_showing_ = true;
+ }
+
+ virtual void OnPopupHidden() OVERRIDE {
+ popup_showing_ = false;
+ }
+
+ bool popup_showing() { return popup_showing_; }
+
+ private:
+ bool popup_showing_;
+};
+
+} // namespace
+
+class PasswordGenerationInteractiveTest : public InProcessBrowserTest {
+ public:
+ virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
+ // Make sure the feature is enabled.
+ command_line->AppendSwitch(autofill::switches::kEnablePasswordGeneration);
+
+ // Don't require ping from autofill or blacklist checking.
+ command_line->AppendSwitch(
+ autofill::switches::kLocalHeuristicsOnlyForPasswordGeneration);
+ }
+
+ virtual void SetUpOnMainThread() OVERRIDE {
+ // Disable Autofill requesting access to AddressBook data. This will cause
+ // test tests to hang on Mac.
+ autofill::test::DisableSystemServices(browser()->profile());
+
+ // Set observer for popup.
+ PasswordGenerationManager* generation_manager =
+ PasswordGenerationManager::FromWebContents(GetWebContents());
+ generation_manager->SetTestObserver(&observer_);
+
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
+ GURL url = embedded_test_server()->GetURL("/password/signup_form.html");
+ ui_test_utils::NavigateToURL(browser(), url);
+ }
+
+ virtual void CleanUpOnMainThread() OVERRIDE {
+ // Cleanup UI.
+ PasswordGenerationManager* generation_manager =
+ PasswordGenerationManager::FromWebContents(GetWebContents());
+ generation_manager->HidePopup();
+ }
+
+ content::WebContents* GetWebContents() {
+ return browser()->tab_strip_model()->GetActiveWebContents();
+ }
+
+ content::RenderViewHost* GetRenderViewHost() {
+ return GetWebContents()->GetRenderViewHost();
+ }
+
+ std::string GetFieldValue(const std::string& field_id) {
+ std::string value;
+ EXPECT_TRUE(content::ExecuteScriptAndExtractString(
+ GetRenderViewHost(),
+ "window.domAutomationController.send("
+ " document.getElementById('" + field_id + "').value);",
+ &value));
+ return value;
+ }
+
+ void FocusPasswordField() {
+ ASSERT_TRUE(content::ExecuteScript(
+ GetRenderViewHost(),
+ "document.getElementById('password_field').focus()"));
+ }
+
+ void SendKeyToPopup(ui::KeyboardCode key) {
+ content::NativeWebKeyboardEvent event;
+ event.windowsKeyCode = key;
+ event.type = blink::WebKeyboardEvent::RawKeyDown;
+ GetRenderViewHost()->ForwardKeyboardEvent(event);
+ }
+
+ bool popup_showing() {
+ return observer_.popup_showing();
+ }
+
+ private:
+ TestPopupObserver observer_;
+};
+
+#if defined(USE_AURA)
+// Enabled on these platforms
+#define MAYBE_PopupShownAndPasswordSelected PopupShownAndPasswordSelected
+#define MAYBE_PopupShownAndDismissed PopupShownAndDismissed
+#else
+// Popup not enabled for these platforms yet.
+#define MAYBE_PopupShownAndPasswordSelected DISABLED_PopupShownAndPasswordSelected
+#define MAYBE_PopupShownAndDismissed DISABLED_PopupShownAndDismissed
+#endif
+
+IN_PROC_BROWSER_TEST_F(PasswordGenerationInteractiveTest,
+ MAYBE_PopupShownAndPasswordSelected) {
+ FocusPasswordField();
+ EXPECT_TRUE(popup_showing());
+ SendKeyToPopup(ui::VKEY_DOWN);
+ SendKeyToPopup(ui::VKEY_RETURN);
+
+ EXPECT_FALSE(GetFieldValue("password_field").empty());
+}
+
+IN_PROC_BROWSER_TEST_F(PasswordGenerationInteractiveTest,
+ MAYBE_PopupShownAndDismissed) {
+ FocusPasswordField();
+ EXPECT_TRUE(popup_showing());
+
+ SendKeyToPopup(ui::VKEY_ESCAPE);
+
+ // Popup is dismissed.
+ EXPECT_FALSE(popup_showing());
+}
diff --git a/chrome/browser/password_manager/password_generation_manager.cc b/chrome/browser/password_manager/password_generation_manager.cc
index 0528120..d808b2f 100644
--- a/chrome/browser/password_manager/password_generation_manager.cc
+++ b/chrome/browser/password_manager/password_generation_manager.cc
@@ -8,6 +8,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
+#include "chrome/browser/ui/autofill/password_generation_popup_controller_impl.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
@@ -21,6 +22,7 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_view.h"
#include "ipc/ipc_message_macros.h"
#include "ui/gfx/rect.h"
@@ -28,10 +30,16 @@ DEFINE_WEB_CONTENTS_USER_DATA_KEY(PasswordGenerationManager);
PasswordGenerationManager::PasswordGenerationManager(
content::WebContents* contents)
- : content::WebContentsObserver(contents) {}
+ : content::WebContentsObserver(contents),
+ observer_(NULL) {}
PasswordGenerationManager::~PasswordGenerationManager() {}
+void PasswordGenerationManager::SetTestObserver(
+ autofill::PasswordGenerationPopupObserver* observer) {
+ observer_ = observer;
+}
+
void PasswordGenerationManager::DetectAccountCreationForms(
const std::vector<autofill::FormStructure*>& forms) {
std::vector<autofill::FormData> account_creation_forms;
@@ -58,6 +66,10 @@ bool PasswordGenerationManager::OnMessageReceived(const IPC::Message& message) {
IPC_BEGIN_MESSAGE_MAP(PasswordGenerationManager, message)
IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowPasswordGenerationPopup,
OnShowPasswordGenerationPopup)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowPasswordEditingPopup,
+ OnShowPasswordEditingPopup)
+ IPC_MESSAGE_HANDLER(AutofillHostMsg_HidePasswordGenerationPopup,
+ OnHidePasswordGenerationPopup)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
@@ -102,17 +114,50 @@ void PasswordGenerationManager::SendAccountCreationFormsToRenderer(
host->GetRoutingID(), forms));
}
+gfx::RectF PasswordGenerationManager::GetBoundsInScreenSpace(
+ const gfx::RectF& bounds) {
+ gfx::Rect client_area;
+ web_contents()->GetView()->GetContainerBounds(&client_area);
+ return bounds + client_area.OffsetFromOrigin();
+}
+
void PasswordGenerationManager::OnShowPasswordGenerationPopup(
- const gfx::Rect& bounds,
+ const gfx::RectF& bounds,
int max_length,
const autofill::PasswordForm& form) {
-#if defined(OS_ANDROID)
- NOTIMPLEMENTED();
-#else
+ // TODO(gcasto): Validate data in PasswordForm.
+
+ // Only implemented for Aura right now.
+#if defined(USE_AURA)
+ // Convert element_bounds to be in screen space.
+ gfx::RectF element_bounds_in_screen_space = GetBoundsInScreenSpace(bounds);
+
password_generator_.reset(new autofill::PasswordGenerator(max_length));
- Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
- browser->window()->ShowPasswordGenerationBubble(bounds,
- form,
- password_generator_.get());
-#endif // #if defined(OS_ANDROID)
+
+ popup_controller_ =
+ autofill::PasswordGenerationPopupControllerImpl::GetOrCreate(
+ popup_controller_,
+ element_bounds_in_screen_space,
+ form,
+ password_generator_.get(),
+ PasswordManager::FromWebContents(web_contents()),
+ observer_,
+ web_contents(),
+ web_contents()->GetView()->GetNativeView());
+ popup_controller_->Show();
+#endif // #if defined(USE_AURA)
+}
+
+void PasswordGenerationManager::OnShowPasswordEditingPopup(
+ const gfx::RectF& bounds) {
+ // TODO(gcasto): Enable this.
+}
+
+void PasswordGenerationManager::OnHidePasswordGenerationPopup() {
+ HidePopup();
+}
+
+void PasswordGenerationManager::HidePopup() {
+ if (popup_controller_)
+ popup_controller_->HideAndDestroy();
}
diff --git a/chrome/browser/password_manager/password_generation_manager.h b/chrome/browser/password_manager/password_generation_manager.h
index 6d0a7ff..3eb4cbd 100644
--- a/chrome/browser/password_manager/password_generation_manager.h
+++ b/chrome/browser/password_manager/password_generation_manager.h
@@ -7,13 +7,17 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
+#include "ui/gfx/rect.h"
namespace autofill {
struct FormData;
class FormStructure;
class PasswordGenerator;
+class PasswordGenerationPopupControllerImpl;
+class PasswordGenerationPopupObserver;
struct PasswordForm;
}
@@ -46,6 +50,12 @@ class PasswordGenerationManager
void DetectAccountCreationForms(
const std::vector<autofill::FormStructure*>& forms);
+ // Hide any visible password generation related popups.
+ void HidePopup();
+
+ // Observer for PasswordGenerationPopup events. Used for testing.
+ void SetTestObserver(autofill::PasswordGenerationPopupObserver* observer);
+
protected:
explicit PasswordGenerationManager(content::WebContents* contents);
@@ -65,16 +75,33 @@ class PasswordGenerationManager
content::RenderViewHost* host,
const std::vector<autofill::FormData>& forms);
- // Causes the password generation bubble UI to be shown for the specified
- // form. The popup will be anchored at |icon_bounds|. The generated
- // password will be no longer than |max_length|.
- void OnShowPasswordGenerationPopup(const gfx::Rect& icon_bounds,
+ // Given |bounds| in the renderers coordinate system, return the same bounds
+ // in the screens coordinate system.
+ gfx::RectF GetBoundsInScreenSpace(const gfx::RectF& bounds);
+
+ // Causes the password generation UI to be shown for the specified form.
+ // The popup will be anchored at |element_bounds|. The generated password
+ // will be no longer than |max_length|.
+ void OnShowPasswordGenerationPopup(const gfx::RectF& element_bounds,
int max_length,
const autofill::PasswordForm& form);
+ // Causes the password editing UI to be shown anchored at |element_bounds|.
+ void OnShowPasswordEditingPopup(const gfx::RectF& element_bounds);
+
+ // Hide any visible UI.
+ void OnHidePasswordGenerationPopup();
+
+ // Observer for password generation popup.
+ autofill::PasswordGenerationPopupObserver* observer_;
+
// Controls how passwords are generated.
scoped_ptr<autofill::PasswordGenerator> password_generator_;
+ // Controls the popup
+ base::WeakPtr<
+ autofill::PasswordGenerationPopupControllerImpl> popup_controller_;
+
DISALLOW_COPY_AND_ASSIGN(PasswordGenerationManager);
};
diff --git a/chrome/browser/ui/autofill/password_generation_popup_controller_impl.cc b/chrome/browser/ui/autofill/password_generation_popup_controller_impl.cc
index 8d73906..1aa5dd6 100644
--- a/chrome/browser/ui/autofill/password_generation_popup_controller_impl.cc
+++ b/chrome/browser/ui/autofill/password_generation_popup_controller_impl.cc
@@ -105,7 +105,7 @@ bool PasswordGenerationPopupControllerImpl::HandleKeyPressEvent(
return true;
case ui::VKEY_RETURN:
case ui::VKEY_TAB:
- // We supress tab if the password is selected because we will
+ // We suppress tab if the password is selected because we will
// automatically advance focus anyway.
return PossiblyAcceptPassword();
default:
@@ -114,10 +114,12 @@ bool PasswordGenerationPopupControllerImpl::HandleKeyPressEvent(
}
bool PasswordGenerationPopupControllerImpl::PossiblyAcceptPassword() {
- if (password_selected_)
- PasswordAccepted();
+ if (password_selected_) {
+ PasswordAccepted(); // This will delete |this|.
+ return true;
+ }
- return password_selected_;
+ return false;
}
void PasswordGenerationPopupControllerImpl::PasswordSelected(bool selected) {
@@ -207,6 +209,10 @@ void PasswordGenerationPopupControllerImpl::Show() {
observer_->OnPopupShown();
}
+void PasswordGenerationPopupControllerImpl::HideAndDestroy() {
+ Hide();
+}
+
void PasswordGenerationPopupControllerImpl::Hide() {
controller_common_.RemoveKeyPressCallback();
diff --git a/chrome/browser/ui/autofill/password_generation_popup_controller_impl.h b/chrome/browser/ui/autofill/password_generation_popup_controller_impl.h
index 686d786..d36c884 100644
--- a/chrome/browser/ui/autofill/password_generation_popup_controller_impl.h
+++ b/chrome/browser/ui/autofill/password_generation_popup_controller_impl.h
@@ -59,6 +59,9 @@ class PasswordGenerationPopupControllerImpl
// Does not update the view if one is already showing.
void Show();
+ // Hides the popup and destroys |this|.
+ void HideAndDestroy();
+
// Accessors.
content::WebContents* web_contents() {
return controller_common_.web_contents();
diff --git a/chrome/browser/ui/autofill/password_generation_popup_view.cc b/chrome/browser/ui/autofill/password_generation_popup_view.cc
new file mode 100644
index 0000000..dd250ae
--- /dev/null
+++ b/chrome/browser/ui/autofill/password_generation_popup_view.cc
@@ -0,0 +1,21 @@
+// Copyright 2014 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 <cstddef>
+
+#include "chrome/browser/ui/autofill/password_generation_popup_view.h"
+
+// Non-aura is not implemented yet.
+#if !defined(USE_AURA)
+
+namespace autofill {
+
+PasswordGenerationPopupView* PasswordGenerationPopupView::Create(
+ PasswordGenerationPopupController* controller) {
+ return NULL;
+}
+
+} // namespace autofill
+
+#endif // #if !defined(USE_AURA)
diff --git a/chrome/browser/ui/autofill/tab_autofill_manager_delegate.cc b/chrome/browser/ui/autofill/tab_autofill_manager_delegate.cc
index 5e306e8..9c3dfdb 100644
--- a/chrome/browser/ui/autofill/tab_autofill_manager_delegate.cc
+++ b/chrome/browser/ui/autofill/tab_autofill_manager_delegate.cc
@@ -144,6 +144,13 @@ void TabAutofillManagerDelegate::UpdateAutofillPopupDataListValues(
void TabAutofillManagerDelegate::HideAutofillPopup() {
if (popup_controller_.get())
popup_controller_->Hide();
+
+ // Password generation popups behave in the same fashion and should also
+ // be hidden.
+ PasswordGenerationManager* generation_manager =
+ PasswordGenerationManager::FromWebContents(web_contents_);
+ if (generation_manager)
+ generation_manager->HidePopup();
}
bool TabAutofillManagerDelegate::IsAutocompleteEnabled() {
diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi
index 8f99bb2..8235c434 100644
--- a/chrome/chrome_browser_ui.gypi
+++ b/chrome/chrome_browser_ui.gypi
@@ -406,6 +406,7 @@
'browser/ui/autofill/password_generation_popup_controller_impl.h',
'browser/ui/autofill/password_generation_popup_controller.h',
'browser/ui/autofill/password_generation_popup_observer.h',
+ 'browser/ui/autofill/password_generation_popup_view.cc',
'browser/ui/autofill/password_generation_popup_view.h',
'browser/ui/autofill/popup_controller_common.cc',
'browser/ui/autofill/popup_controller_common.h',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 1272abe..39f6e5c 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -187,6 +187,7 @@
'browser/notifications/desktop_notifications_unittest.cc',
'browser/notifications/desktop_notifications_unittest.h',
'browser/notifications/notification_browsertest.cc',
+ 'browser/password_manager/password_generation_interactive_uitest.cc',
'browser/task_manager/task_manager_browsertest_util.cc',
'browser/ui/app_list/app_list_service_interactive_uitest.cc',
'browser/ui/app_list/app_list_service_mac_interactive_uitest.mm',
@@ -953,6 +954,8 @@
'../components/autofill/content/renderer/password_form_conversion_utils_browsertest.cc',
'../components/autofill/content/renderer/test_password_autofill_agent.h',
'../components/autofill/content/renderer/test_password_autofill_agent.cc',
+ '../components/autofill/content/renderer/test_password_generation_agent.h',
+ '../components/autofill/content/renderer/test_password_generation_agent.cc',
'app/chrome_command_ids.h',
'app/chrome_dll.rc',
'app/chrome_dll_resource.h',
@@ -2100,6 +2103,8 @@
'sources': [
'../components/autofill/content/renderer/test_password_autofill_agent.cc',
'../components/autofill/content/renderer/test_password_autofill_agent.h',
+ '../components/autofill/content/renderer/test_password_generation_agent.cc',
+ '../components/autofill/content/renderer/test_password_generation_agent.h',
'app/chrome_command_ids.h',
'app/chrome_dll.rc',
'app/chrome_dll_resource.h',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index e8268bf..77f8003 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -527,6 +527,8 @@
'../apps/shell_window_geometry_cache_unittest.cc',
'../components/autofill/content/renderer/test_password_autofill_agent.cc',
'../components/autofill/content/renderer/test_password_autofill_agent.h',
+ '../components/autofill/content/renderer/test_password_generation_agent.cc',
+ '../components/autofill/content/renderer/test_password_generation_agent.h',
'../extensions/browser/admin_policy_unittest.cc',
'../extensions/browser/error_map_unittest.cc',
'../extensions/browser/event_listener_map_unittest.cc',
diff --git a/chrome/renderer/autofill/password_generation_agent_browsertest.cc b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
index b39c06e..5e24681 100644
--- a/chrome/renderer/autofill/password_generation_agent_browsertest.cc
+++ b/chrome/renderer/autofill/password_generation_agent_browsertest.cc
@@ -5,11 +5,11 @@
#include <string.h>
#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/test/base/chrome_render_view_test.h"
#include "components/autofill/content/common/autofill_messages.h"
-#include "components/autofill/content/renderer/password_generation_agent.h"
+#include "components/autofill/content/renderer/autofill_agent.h"
+#include "components/autofill/content/renderer/test_password_generation_agent.h"
#include "components/autofill/core/common/form_data.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebString.h"
@@ -24,79 +24,21 @@ using blink::WebString;
namespace autofill {
-class TestPasswordGenerationAgent : public PasswordGenerationAgent {
- public:
- explicit TestPasswordGenerationAgent(content::RenderView* view)
- : PasswordGenerationAgent(view) {}
- virtual ~TestPasswordGenerationAgent() {}
-
- // Make this public
- virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
- return PasswordGenerationAgent::OnMessageReceived(message);
- }
-
- const std::vector<IPC::Message*>& messages() {
- return messages_.get();
- }
-
- void ClearMessages() {
- messages_.clear();
- }
-
- protected:
- virtual bool ShouldAnalyzeDocument(const blink::WebDocument& document) const
- OVERRIDE {
- return true;
- }
-
- virtual bool Send(IPC::Message* message) OVERRIDE {
- messages_.push_back(message);
- return true;
- }
-
- private:
- ScopedVector<IPC::Message> messages_;
-
- DISALLOW_COPY_AND_ASSIGN(TestPasswordGenerationAgent);
-};
-
class PasswordGenerationAgentTest : public ChromeRenderViewTest {
public:
PasswordGenerationAgentTest() {}
- virtual void SetUp() {
- // We don't actually create a PasswordGenerationAgent during
- // ChromeRenderViewTest::SetUp because it's behind a flag. Since we want
- // to use a test manager anyway, we just create our own.
- ChromeRenderViewTest::SetUp();
- generation_manager_.reset(new TestPasswordGenerationAgent(view_));
- }
-
virtual void TearDown() {
LoadHTML("");
- generation_manager_.reset();
ChromeRenderViewTest::TearDown();
}
- void SimulateClickOnDecoration(blink::WebInputElement* input_element) {
- generation_manager_->ClearMessages();
- blink::WebElement decoration =
- input_element->decorationElementFor(generation_manager_.get());
- decoration.simulateClick();
- }
-
- bool DecorationIsVisible(blink::WebInputElement* input_element) {
- blink::WebElement decoration =
- input_element->decorationElementFor(generation_manager_.get());
- return decoration.hasNonEmptyBoundingBox();
- }
-
void SetNotBlacklistedMessage(const char* form_str) {
autofill::PasswordForm form;
form.origin =
GURL(base::StringPrintf("data:text/html;charset=utf-8,%s", form_str));
AutofillMsg_FormNotBlacklisted msg(0, form);
- generation_manager_->OnMessageReceived(msg);
+ password_generation_->OnMessageReceived(msg);
}
void SetAccountCreationFormsDetectedMessage(const char* form_str) {
@@ -106,29 +48,29 @@ class PasswordGenerationAgentTest : public ChromeRenderViewTest {
std::vector<autofill::FormData> forms;
forms.push_back(form);
AutofillMsg_AccountCreationFormsDetected msg(0, forms);
- generation_manager_->OnMessageReceived(msg);
+ password_generation_->OnMessageReceived(msg);
}
- void ExpectPasswordGenerationIconShown(const char* element_id, bool shown) {
+ void ExpectPasswordGenerationAvailable(const char* element_id,
+ bool available) {
WebDocument document = GetMainFrame()->document();
WebElement element =
document.getElementById(WebString::fromUTF8(element_id));
ASSERT_FALSE(element.isNull());
WebInputElement target_element = element.to<WebInputElement>();
- if (shown) {
- EXPECT_TRUE(DecorationIsVisible(&target_element));
- SimulateClickOnDecoration(&target_element);
- EXPECT_EQ(1u, generation_manager_->messages().size());
+ ExecuteJavaScript(
+ base::StringPrintf("document.getElementById('%s').focus();",
+ element_id).c_str());
+ if (available) {
+ ASSERT_EQ(1u, password_generation_->messages().size());
EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID,
- generation_manager_->messages()[0]->type());
+ password_generation_->messages()[0]->type());
} else {
- EXPECT_FALSE(DecorationIsVisible(&target_element));
+ EXPECT_EQ(0u, password_generation_->messages().size());
}
+ password_generation_->clear_messages();
}
- protected:
- scoped_ptr<TestPasswordGenerationAgent> generation_manager_;
-
private:
DISALLOW_COPY_AND_ASSIGN(PasswordGenerationAgentTest);
};
@@ -169,11 +111,11 @@ const char kInvalidActionAccountCreationFormHTML[] =
TEST_F(PasswordGenerationAgentTest, DetectionTest) {
// Don't shown the icon for non account creation forms.
LoadHTML(kSigninFormHTML);
- ExpectPasswordGenerationIconShown("password", false);
+ ExpectPasswordGenerationAvailable("password", false);
// We don't show the decoration yet because the feature isn't enabled.
LoadHTML(kAccountCreationFormHTML);
- ExpectPasswordGenerationIconShown("first_password", false);
+ ExpectPasswordGenerationAvailable("first_password", false);
// Pretend like We have received message indicating site is not blacklisted,
// and we have received message indicating the form is classified as
@@ -181,20 +123,20 @@ TEST_F(PasswordGenerationAgentTest, DetectionTest) {
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
- ExpectPasswordGenerationIconShown("first_password", true);
+ ExpectPasswordGenerationAvailable("first_password", true);
// This doesn't trigger because hidden password fields are ignored.
LoadHTML(kHiddenPasswordAccountCreationFormHTML);
SetNotBlacklistedMessage(kHiddenPasswordAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(
kHiddenPasswordAccountCreationFormHTML);
- ExpectPasswordGenerationIconShown("first_password", false);
+ ExpectPasswordGenerationAvailable("first_password", false);
// This doesn't trigger because the form action is invalid.
LoadHTML(kInvalidActionAccountCreationFormHTML);
SetNotBlacklistedMessage(kInvalidActionAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(kInvalidActionAccountCreationFormHTML);
- ExpectPasswordGenerationIconShown("first_password", false);
+ ExpectPasswordGenerationAvailable("first_password", false);
}
TEST_F(PasswordGenerationAgentTest, FillTest) {
@@ -216,7 +158,7 @@ TEST_F(PasswordGenerationAgentTest, FillTest) {
base::string16 password = base::ASCIIToUTF16("random_password");
AutofillMsg_GeneratedPasswordAccepted msg(0, password);
- generation_manager_->OnMessageReceived(msg);
+ password_generation_->OnMessageReceived(msg);
// Password fields are filled out and set as being autofilled.
EXPECT_EQ(password, first_password_element.value());
@@ -232,26 +174,62 @@ TEST_F(PasswordGenerationAgentTest, FillTest) {
EXPECT_EQ(element, document.focusedNode());
}
+TEST_F(PasswordGenerationAgentTest, EditingTest) {
+ LoadHTML(kAccountCreationFormHTML);
+ SetNotBlacklistedMessage(kAccountCreationFormHTML);
+ SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
+
+ WebDocument document = GetMainFrame()->document();
+ WebElement element =
+ document.getElementById(WebString::fromUTF8("first_password"));
+ ASSERT_FALSE(element.isNull());
+ WebInputElement first_password_element = element.to<WebInputElement>();
+ element = document.getElementById(WebString::fromUTF8("second_password"));
+ ASSERT_FALSE(element.isNull());
+ WebInputElement second_password_element = element.to<WebInputElement>();
+
+ base::string16 password = base::ASCIIToUTF16("random_password");
+ AutofillMsg_GeneratedPasswordAccepted msg(0, password);
+ password_generation_->OnMessageReceived(msg);
+
+ // Passwords start out the same.
+ EXPECT_EQ(password, first_password_element.value());
+ EXPECT_EQ(password, second_password_element.value());
+
+ // After editing the first field they are still the same.
+ base::string16 edited_password = base::ASCIIToUTF16("edited_password");
+ first_password_element.setValue(edited_password);
+ // Cast to WebAutofillClient where textFieldDidChange() is public.
+ static_cast<blink::WebAutofillClient*>(autofill_agent_)->textFieldDidChange(
+ first_password_element);
+ // textFieldDidChange posts a task, so we need to wait until it's been
+ // processed.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ EXPECT_EQ(edited_password, first_password_element.value());
+ EXPECT_EQ(edited_password, second_password_element.value());
+}
+
TEST_F(PasswordGenerationAgentTest, BlacklistedTest) {
// Did not receive not blacklisted message. Don't show password generation
// icon.
LoadHTML(kAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
- ExpectPasswordGenerationIconShown("first_password", false);
+ ExpectPasswordGenerationAvailable("first_password", false);
// Receive one not blacklisted message for non account creation form. Don't
// show password generation icon.
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kSigninFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
- ExpectPasswordGenerationIconShown("first_password", false);
+ ExpectPasswordGenerationAvailable("first_password", false);
// Receive one not blackliste message for account creation form. Show password
// generation icon.
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
- ExpectPasswordGenerationIconShown("first_password", true);
+ ExpectPasswordGenerationAvailable("first_password", true);
// Receive two not blacklisted messages, one is for account creation form and
// the other is not. Show password generation icon.
@@ -259,7 +237,7 @@ TEST_F(PasswordGenerationAgentTest, BlacklistedTest) {
SetNotBlacklistedMessage(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kSigninFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
- ExpectPasswordGenerationIconShown("first_password", true);
+ ExpectPasswordGenerationAvailable("first_password", true);
}
TEST_F(PasswordGenerationAgentTest, AccountCreationFormsDetectedTest) {
@@ -267,14 +245,14 @@ TEST_F(PasswordGenerationAgentTest, AccountCreationFormsDetectedTest) {
// password generation icon.
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kAccountCreationFormHTML);
- ExpectPasswordGenerationIconShown("first_password", false);
+ ExpectPasswordGenerationAvailable("first_password", false);
// Receive the account creation forms detected message. Show password
// generation icon.
LoadHTML(kAccountCreationFormHTML);
SetNotBlacklistedMessage(kAccountCreationFormHTML);
SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML);
- ExpectPasswordGenerationIconShown("first_password", true);
+ ExpectPasswordGenerationAvailable("first_password", true);
}
} // namespace autofill
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 9ea0b8f..870b7a3 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -68,7 +68,6 @@
#include "components/autofill/content/renderer/autofill_agent.h"
#include "components/autofill/content/renderer/password_autofill_agent.h"
#include "components/autofill/content/renderer/password_generation_agent.h"
-#include "components/autofill/core/common/password_generation_util.h"
#include "components/nacl/renderer/ppb_nacl_private_impl.h"
#include "components/plugins/renderer/mobile_youtube_plugin.h"
#include "components/visitedlink/renderer/visitedlink_slave.h"
@@ -406,13 +405,15 @@ void ChromeContentRendererClient::RenderViewCreated(
safe_browsing::MalwareDOMDetails::Create(render_view);
#endif
+ PasswordGenerationAgent* password_generation_agent =
+ new PasswordGenerationAgent(render_view);
PasswordAutofillAgent* password_autofill_agent =
new PasswordAutofillAgent(render_view);
- new AutofillAgent(render_view, password_autofill_agent);
+ new AutofillAgent(render_view,
+ password_autofill_agent,
+ password_generation_agent);
CommandLine* command_line = CommandLine::ForCurrentProcess();
- if (autofill::password_generation::IsPasswordGenerationEnabled())
- new PasswordGenerationAgent(render_view);
if (command_line->HasSwitch(switches::kInstantProcess))
new SearchBox(render_view);
diff --git a/chrome/test/base/chrome_render_view_test.cc b/chrome/test/base/chrome_render_view_test.cc
index 47e06f9..e92236f 100644
--- a/chrome/test/base/chrome_render_view_test.cc
+++ b/chrome/test/base/chrome_render_view_test.cc
@@ -14,6 +14,7 @@
#include "components/autofill/content/renderer/autofill_agent.h"
#include "components/autofill/content/renderer/password_autofill_agent.h"
#include "components/autofill/content/renderer/test_password_autofill_agent.h"
+#include "components/autofill/content/renderer/test_password_generation_agent.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/common/renderer_preferences.h"
#include "content/public/renderer/render_view.h"
@@ -40,6 +41,7 @@ using blink::WebString;
using blink::WebURLRequest;
using autofill::AutofillAgent;
using autofill::PasswordAutofillAgent;
+using autofill::PasswordGenerationAgent;
ChromeRenderViewTest::ChromeRenderViewTest() : extension_dispatcher_(NULL) {
}
@@ -60,11 +62,13 @@ void ChromeRenderViewTest::SetUp() {
content::RenderViewTest::SetUp();
- // RenderView doesn't expose its PasswordAutofillAgent or AutofillAgent
- // objects, because it has no need to store them directly (they're stored as
- // RenderViewObserver*). So just create another set.
+ // RenderView doesn't expose its Agent objects, because it has no need to
+ // store them directly (they're stored as RenderViewObserver*). So just
+ // create another set.
password_autofill_ = new autofill::TestPasswordAutofillAgent(view_);
- autofill_agent_ = new AutofillAgent(view_, password_autofill_);
+ password_generation_ = new autofill::TestPasswordGenerationAgent(view_);
+ autofill_agent_ =
+ new AutofillAgent(view_, password_autofill_, password_generation_);
}
void ChromeRenderViewTest::TearDown() {
diff --git a/chrome/test/base/chrome_render_view_test.h b/chrome/test/base/chrome_render_view_test.h
index 7e1e6c3..aff4c5d 100644
--- a/chrome/test/base/chrome_render_view_test.h
+++ b/chrome/test/base/chrome_render_view_test.h
@@ -14,6 +14,7 @@
namespace autofill {
class AutofillAgent;
class TestPasswordAutofillAgent;
+class TestPasswordGenerationAgent;
}
namespace extensions {
@@ -34,6 +35,7 @@ class ChromeRenderViewTest : public content::RenderViewTest {
extensions::Dispatcher* extension_dispatcher_;
autofill::TestPasswordAutofillAgent* password_autofill_;
+ autofill::TestPasswordGenerationAgent* password_generation_;
autofill::AutofillAgent* autofill_agent_;
// Naked pointer as ownership is with content::RenderViewTest::render_thread_.
diff --git a/components/autofill/content/common/autofill_messages.h b/components/autofill/content/common/autofill_messages.h
index 2e6e960..54fd250 100644
--- a/components/autofill/content/common/autofill_messages.h
+++ b/components/autofill/content/common/autofill_messages.h
@@ -232,14 +232,22 @@ IPC_MESSAGE_ROUTED0(AutofillHostMsg_DidEndTextFieldEditing)
// Instructs the browser to hide the Autofill UI.
IPC_MESSAGE_ROUTED0(AutofillHostMsg_HideAutofillUI)
-// Instructs the browser to show the password generation bubble at the
+// Instructs the browser to show the password generation popup at the
// specified location. This location should be specified in the renderers
// coordinate system. Form is the form associated with the password field.
IPC_MESSAGE_ROUTED3(AutofillHostMsg_ShowPasswordGenerationPopup,
- gfx::Rect /* source location */,
+ gfx::RectF /* source location */,
int /* max length of the password */,
autofill::PasswordForm)
+// Instructs the browser to show the popup for editing a generated password.
+// The location should be specified in the renderers coordinate system.
+IPC_MESSAGE_ROUTED1(AutofillHostMsg_ShowPasswordEditingPopup,
+ gfx::RectF /* source location */)
+
+// Instructs the browser to hide any password generation popups.
+IPC_MESSAGE_ROUTED0(AutofillHostMsg_HidePasswordGenerationPopup)
+
// Instruct the browser that a password mapping has been found for a field.
IPC_MESSAGE_ROUTED2(AutofillHostMsg_AddPasswordFormMapping,
autofill::FormFieldData, /* the user name field */
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc
index 1b37ce2..979976f 100644
--- a/components/autofill/content/renderer/autofill_agent.cc
+++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -15,6 +15,7 @@
#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/page_click_tracker.h"
#include "components/autofill/content/renderer/password_autofill_agent.h"
+#include "components/autofill/content/renderer/password_generation_agent.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_data_validation.h"
#include "components/autofill/core/common/autofill_switches.h"
@@ -109,20 +110,14 @@ void TrimStringVectorForIPC(std::vector<base::string16>* strings) {
}
}
-gfx::RectF GetScaledBoundingBox(float scale, WebInputElement* element) {
- gfx::Rect bounding_box(element->boundsInViewportSpace());
- return gfx::RectF(bounding_box.x() * scale,
- bounding_box.y() * scale,
- bounding_box.width() * scale,
- bounding_box.height() * scale);
-}
-
} // namespace
AutofillAgent::AutofillAgent(content::RenderView* render_view,
- PasswordAutofillAgent* password_autofill_agent)
+ PasswordAutofillAgent* password_autofill_agent,
+ PasswordGenerationAgent* password_generation_agent)
: content::RenderViewObserver(render_view),
password_autofill_agent_(password_autofill_agent),
+ password_generation_agent_(password_generation_agent),
autofill_query_id_(0),
autofill_action_(AUTOFILL_NONE),
web_view_(render_view->GetWebView()),
@@ -334,6 +329,11 @@ void AutofillAgent::TextFieldDidChangeImpl(const WebInputElement& element) {
if (!element.focused())
return;
+ if (password_generation_agent_ &&
+ password_generation_agent_->TextDidChangeInTextField(element)) {
+ return;
+ }
+
if (password_autofill_agent_->TextDidChangeInTextField(element)) {
element_ = element;
return;
diff --git a/components/autofill/content/renderer/autofill_agent.h b/components/autofill/content/renderer/autofill_agent.h
index 7bc7f60..cb4df03 100644
--- a/components/autofill/content/renderer/autofill_agent.h
+++ b/components/autofill/content/renderer/autofill_agent.h
@@ -32,6 +32,7 @@ struct FormData;
struct FormFieldData;
struct WebElementDescriptor;
class PasswordAutofillAgent;
+class PasswordGenerationAgent;
// AutofillAgent deals with Autofill related communications between WebKit and
// the browser. There is one AutofillAgent per RenderView.
@@ -46,8 +47,11 @@ class AutofillAgent : public content::RenderViewObserver,
public blink::WebAutofillClient {
public:
// PasswordAutofillAgent is guaranteed to outlive AutofillAgent.
+ // PasswordGenerationAgent may be NULL. If it is not, then it is also
+ // guaranteed to outlive AutofillAgent.
AutofillAgent(content::RenderView* render_view,
- PasswordAutofillAgent* password_autofill_manager);
+ PasswordAutofillAgent* password_autofill_manager,
+ PasswordGenerationAgent* password_generation_agent);
virtual ~AutofillAgent();
private:
@@ -174,7 +178,8 @@ class AutofillAgent : public content::RenderViewObserver,
FormCache form_cache_;
- PasswordAutofillAgent* password_autofill_agent_; // WEAK reference.
+ PasswordAutofillAgent* password_autofill_agent_; // Weak reference.
+ PasswordGenerationAgent* password_generation_agent_; // Weak reference.
// The ID of the last request sent for form field Autofill. Used to ignore
// out of date responses.
diff --git a/components/autofill/content/renderer/form_autofill_util.cc b/components/autofill/content/renderer/form_autofill_util.cc
index 74d5b68..5d1f379 100644
--- a/components/autofill/content/renderer/form_autofill_util.cc
+++ b/components/autofill/content/renderer/form_autofill_util.cc
@@ -1175,4 +1175,12 @@ bool IsWebElementEmpty(const blink::WebElement& element) {
return true;
}
+gfx::RectF GetScaledBoundingBox(float scale, WebInputElement* element) {
+ gfx::Rect bounding_box(element->boundsInViewportSpace());
+ return gfx::RectF(bounding_box.x() * scale,
+ bounding_box.y() * scale,
+ bounding_box.width() * scale,
+ bounding_box.height() * scale);
+}
+
} // namespace autofill
diff --git a/components/autofill/content/renderer/form_autofill_util.h b/components/autofill/content/renderer/form_autofill_util.h
index 3913481..f3384f1 100644
--- a/components/autofill/content/renderer/form_autofill_util.h
+++ b/components/autofill/content/renderer/form_autofill_util.h
@@ -8,6 +8,7 @@
#include <vector>
#include "base/strings/string16.h"
+#include "ui/gfx/rect.h"
namespace blink {
class WebDocument;
@@ -167,6 +168,9 @@ bool IsWebpageEmpty(const blink::WebFrame* frame);
// are of the type <script>, <meta>, or <title>.
bool IsWebElementEmpty(const blink::WebElement& element);
+// Return a gfx::RectF that is the bounding box for |element| scaled by |scale|.
+gfx::RectF GetScaledBoundingBox(float scale, blink::WebInputElement* element);
+
} // namespace autofill
#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_FORM_AUTOFILL_UTIL_H_
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc
index da9b863..f7e01d1 100644
--- a/components/autofill/content/renderer/password_generation_agent.cc
+++ b/components/autofill/content/renderer/password_generation_agent.cc
@@ -8,6 +8,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "components/autofill/content/common/autofill_messages.h"
+#include "components/autofill/content/renderer/form_autofill_util.h"
#include "components/autofill/content/renderer/password_form_conversion_utils.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/form_data.h"
@@ -98,7 +99,11 @@ bool ContainsForm(const std::vector<autofill::FormData>& forms,
PasswordGenerationAgent::PasswordGenerationAgent(
content::RenderView* render_view)
: content::RenderViewObserver(render_view),
- render_view_(render_view) {
+ render_view_(render_view),
+ password_is_generated_(false),
+ password_edited_(false),
+ enabled_(password_generation::IsPasswordGenerationEnabled()) {
+ DVLOG(2) << "Password Generation is " << (enabled_ ? "Enabled" : "Disabled");
render_view_->GetWebView()->setPasswordGeneratorClient(this);
}
PasswordGenerationAgent::~PasswordGenerationAgent() {}
@@ -108,21 +113,28 @@ void PasswordGenerationAgent::DidFinishDocumentLoad(blink::WebFrame* frame) {
// to query whether the current form is blacklisted or not happens when the
// document load finishes, so we need to clear previous states here before we
// hear back from the browser. We only clear this state on main frame load
- // as we don't want subframe loads to clear state that we have recieved from
+ // as we don't want subframe loads to clear state that we have received from
// the main frame. Note that we assume there is only one account creation
// form, but there could be multiple password forms in each frame.
- //
- // TODO(zysxqn): Add stat when local heuristic fires but we don't show the
- // password generation icon.
if (!frame->parent()) {
not_blacklisted_password_form_origins_.clear();
generation_enabled_forms_.clear();
+ generation_element_.reset();
possible_account_creation_form_.reset(new PasswordForm());
- passwords_.clear();
+ password_elements_.clear();
+ password_is_generated_ = false;
+ if (password_edited_) {
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::PASSWORD_EDITED);
+ }
+ password_edited_ = false;
}
}
void PasswordGenerationAgent::DidFinishLoad(blink::WebFrame* frame) {
+ if (!enabled_)
+ return;
+
// We don't want to generate passwords if the browser won't store or sync
// them.
if (!ShouldAnalyzeDocument(frame->document()))
@@ -154,9 +166,9 @@ void PasswordGenerationAgent::DidFinishLoad(blink::WebFrame* frame) {
DVLOG(2) << "Account creation form detected";
password_generation::LogPasswordGenerationEvent(
password_generation::SIGN_UP_DETECTED);
- passwords_ = passwords;
+ password_elements_ = passwords;
possible_account_creation_form_.swap(password_form);
- MaybeShowIcon();
+ DetermineGenerationElement();
// We assume that there is only one account creation field per URL.
return;
}
@@ -191,8 +203,6 @@ void PasswordGenerationAgent::openPasswordGenerator(
rect,
element.maxLength(),
*password_form));
- password_generation::LogPasswordGenerationEvent(
- password_generation::BUBBLE_SHOWN);
}
bool PasswordGenerationAgent::OnMessageReceived(const IPC::Message& message) {
@@ -211,13 +221,17 @@ bool PasswordGenerationAgent::OnMessageReceived(const IPC::Message& message) {
void PasswordGenerationAgent::OnFormNotBlacklisted(const PasswordForm& form) {
not_blacklisted_password_form_origins_.push_back(form.origin);
- MaybeShowIcon();
+ DetermineGenerationElement();
}
void PasswordGenerationAgent::OnPasswordAccepted(
const base::string16& password) {
- for (std::vector<blink::WebInputElement>::iterator it = passwords_.begin();
- it != passwords_.end(); ++it) {
+ password_is_generated_ = true;
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::PASSWORD_ACCEPTED);
+ for (std::vector<blink::WebInputElement>::iterator it =
+ password_elements_.begin();
+ it != password_elements_.end(); ++it) {
it->setValue(password);
it->setAutofilled(true);
// Advance focus to the next input field. We assume password fields in
@@ -230,45 +244,130 @@ void PasswordGenerationAgent::OnAccountCreationFormsDetected(
const std::vector<autofill::FormData>& forms) {
generation_enabled_forms_.insert(
generation_enabled_forms_.end(), forms.begin(), forms.end());
- MaybeShowIcon();
+ DetermineGenerationElement();
}
-void PasswordGenerationAgent::MaybeShowIcon() {
+void PasswordGenerationAgent::DetermineGenerationElement() {
// Make sure local heuristics have identified a possible account creation
// form.
- if (!possible_account_creation_form_.get() || passwords_.empty()) {
+ if (!possible_account_creation_form_.get() || password_elements_.empty()) {
DVLOG(2) << "Local hueristics have not detected a possible account "
<< "creation form";
return;
}
- // Verify that it's not blacklisted.
- if (not_blacklisted_password_form_origins_.empty() ||
- !ContainsURL(not_blacklisted_password_form_origins_,
- possible_account_creation_form_->origin)) {
- DVLOG(2) << "Have not recieved confirmation that password form isn't "
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kLocalHeuristicsOnlyForPasswordGeneration)) {
+ DVLOG(2) << "Bypassing additional checks.";
+ } else if (not_blacklisted_password_form_origins_.empty() ||
+ !ContainsURL(not_blacklisted_password_form_origins_,
+ possible_account_creation_form_->origin)) {
+ DVLOG(2) << "Have not received confirmation that password form isn't "
<< "blacklisted";
return;
+ } else if (generation_enabled_forms_.empty() ||
+ !ContainsForm(generation_enabled_forms_,
+ *possible_account_creation_form_)) {
+ // Note that this message will never be sent if this feature is disabled
+ // (e.g. Password saving is disabled).
+ DVLOG(2) << "Have not received confirmation from Autofill that form is "
+ << "used for account creation";
+ return;
}
- // Ensure that we get a ping from Autofill saying that this form is used for
- // account creation. Note that this message will not be set if this feature
- // is not enabled. If kNoAutofillNecessaryForPasswordGeneration is set,
- // skip this check. This switch should only be used in testing environments.
- if (!CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kNoAutofillNecessaryForPasswordGeneration) &&
- (generation_enabled_forms_.empty() ||
- !ContainsForm(generation_enabled_forms_,
- *possible_account_creation_form_))) {
- DVLOG(2) << "Have not recieved confirmation from Autofill that form is used"
- << " for account creation";
+ DVLOG(2) << "Password generation eligible form found";
+ generation_element_ = password_elements_[0];
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::GENERATION_AVAILABLE);
+}
+
+void PasswordGenerationAgent::FocusedNodeChanged(const blink::WebNode& node) {
+ // TODO(gcasto): Re-hide generation_element text.
+ if (node.isNull() || !node.isElementNode())
return;
+
+ const blink::WebElement web_element = node.toConst<blink::WebElement>();
+ if (!web_element.document().frame())
+ return;
+
+ const blink::WebInputElement* element = toWebInputElement(&web_element);
+ if (!element || *element != generation_element_)
+ return;
+
+ if (password_is_generated_) {
+ // TODO(gcasto): Make characters visible.
+ ShowEditingPopup();
+ }
+
+ // Only trigger if the password field is empty.
+ if (!element->isReadOnly() &&
+ element->isEnabled() &&
+ element->value().isEmpty()) {
+ ShowGenerationPopup();
+ }
+}
+
+bool PasswordGenerationAgent::TextDidChangeInTextField(
+ const blink::WebInputElement& element) {
+ if (element != generation_element_)
+ return false;
+
+ if (element.value().isEmpty()) {
+ if (password_is_generated_) {
+ // User generated a password and then deleted it.
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::PASSWORD_DELETED);
+ }
+
+ // TODO(gcasto): Set PasswordForm::type in the browser to TYPE_NORMAL.
+ password_is_generated_ = false;
+ // Offer generation again.
+ ShowGenerationPopup();
+ } else if (!password_is_generated_) {
+ // User has rejected the feature and has started typing a password.
+ HidePopup();
+ } else {
+ password_edited_ = true;
+ // Mirror edits to any confirmation password fields.
+ for (std::vector<blink::WebInputElement>::iterator it =
+ password_elements_.begin();
+ it != password_elements_.end(); ++it) {
+ it->setValue(element.value());
+ }
}
- passwords_[0].passwordGeneratorButtonElement().setAttribute("style",
- "display:block");
+ return true;
+}
+
+void PasswordGenerationAgent::ShowGenerationPopup() {
+ gfx::RectF bounding_box_scaled =
+ GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(),
+ &generation_element_);
+
+ Send(new AutofillHostMsg_ShowPasswordGenerationPopup(
+ routing_id(),
+ bounding_box_scaled,
+ generation_element_.maxLength(),
+ *possible_account_creation_form_));
+
password_generation::LogPasswordGenerationEvent(
- password_generation::ICON_SHOWN);
+ password_generation::GENERATION_POPUP_SHOWN);
+}
+
+void PasswordGenerationAgent::ShowEditingPopup() {
+ gfx::RectF bounding_box_scaled =
+ GetScaledBoundingBox(render_view_->GetWebView()->pageScaleFactor(),
+ &generation_element_);
+
+ Send(new AutofillHostMsg_ShowPasswordEditingPopup(routing_id(),
+ bounding_box_scaled));
+
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::EDITING_POPUP_SHOWN);
+}
+
+void PasswordGenerationAgent::HidePopup() {
+ Send(new AutofillHostMsg_HidePasswordGenerationPopup(routing_id()));
}
} // namespace autofill
diff --git a/components/autofill/content/renderer/password_generation_agent.h b/components/autofill/content/renderer/password_generation_agent.h
index b632902..dcb0b19 100644
--- a/components/autofill/content/renderer/password_generation_agent.h
+++ b/components/autofill/content/renderer/password_generation_agent.h
@@ -34,6 +34,10 @@ class PasswordGenerationAgent : public content::RenderViewObserver,
explicit PasswordGenerationAgent(content::RenderView* render_view);
virtual ~PasswordGenerationAgent();
+ // Returns true if the field being changed is one where a generated password
+ // is being offered. Updates the state of the popup if necessary.
+ bool TextDidChangeInTextField(const blink::WebInputElement& element);
+
protected:
// Returns true if this document is one that we should consider analyzing.
// Virtual so that it can be overriden during testing.
@@ -42,10 +46,14 @@ class PasswordGenerationAgent : public content::RenderViewObserver,
// RenderViewObserver:
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ // Use to force enable during testing.
+ void set_enabled(bool enabled) { enabled_ = enabled; }
+
private:
// RenderViewObserver:
virtual void DidFinishDocumentLoad(blink::WebFrame* frame) OVERRIDE;
virtual void DidFinishLoad(blink::WebFrame* frame) OVERRIDE;
+ virtual void FocusedNodeChanged(const blink::WebNode& node) OVERRIDE;
// WebPasswordGeneratorClient:
virtual void openPasswordGenerator(blink::WebInputElement& element) OVERRIDE;
@@ -56,8 +64,19 @@ class PasswordGenerationAgent : public content::RenderViewObserver,
void OnAccountCreationFormsDetected(
const std::vector<autofill::FormData>& forms);
- // Helper function to decide whether we should show password generation icon.
- void MaybeShowIcon();
+ // Helper function to decide if |passwords_| contains password fields for
+ // an account creation form. Sets |generation_element_| to the field that
+ // we want to trigger the generation UI on.
+ void DetermineGenerationElement();
+
+ // Show password generation UI anchored at |generation_element_|.
+ void ShowGenerationPopup();
+
+ // Show UI for editing a generated password at |generation_element_|.
+ void ShowEditingPopup();
+
+ // Hides a password generation popup if one exists.
+ void HidePopup();
content::RenderView* render_view_;
@@ -74,7 +93,22 @@ class PasswordGenerationAgent : public content::RenderViewObserver,
// not be sent if the feature is disabled.
std::vector<autofill::FormData> generation_enabled_forms_;
- std::vector<blink::WebInputElement> passwords_;
+ // Password elements that may be part of an account creation form.
+ std::vector<blink::WebInputElement> password_elements_;
+
+ // Element where we want to trigger password generation UI.
+ blink::WebInputElement generation_element_;
+
+ // If the password field at |generation_element_| contains a generated
+ // password.
+ bool password_is_generated_;
+
+ // True if a password was generated and the user edited it. Used for UMA
+ // stats.
+ bool password_edited_;
+
+ // If this feature is enabled. Controlled by Finch.
+ bool enabled_;
DISALLOW_COPY_AND_ASSIGN(PasswordGenerationAgent);
};
diff --git a/components/autofill/content/renderer/test_password_generation_agent.cc b/components/autofill/content/renderer/test_password_generation_agent.cc
new file mode 100644
index 0000000..2e4adb9
--- /dev/null
+++ b/components/autofill/content/renderer/test_password_generation_agent.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 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 "components/autofill/content/renderer/test_password_generation_agent.h"
+
+namespace autofill {
+
+TestPasswordGenerationAgent::TestPasswordGenerationAgent(
+ content::RenderView* render_view)
+ : PasswordGenerationAgent(render_view) {
+ // Always enable when testing.
+ set_enabled(true);
+}
+
+TestPasswordGenerationAgent::~TestPasswordGenerationAgent() {}
+
+bool TestPasswordGenerationAgent::OnMessageReceived(
+ const IPC::Message& message) {
+ return PasswordGenerationAgent::OnMessageReceived(message);
+}
+
+bool TestPasswordGenerationAgent::ShouldAnalyzeDocument(
+ const blink::WebDocument& document) const {
+ return true;
+}
+
+bool TestPasswordGenerationAgent::Send(IPC::Message* message) {
+ messages_.push_back(message);
+ return true;
+}
+
+} // namespace autofill
diff --git a/components/autofill/content/renderer/test_password_generation_agent.h b/components/autofill/content/renderer/test_password_generation_agent.h
new file mode 100644
index 0000000..e711ab0
--- /dev/null
+++ b/components/autofill/content/renderer/test_password_generation_agent.h
@@ -0,0 +1,44 @@
+// Copyright 2014 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 COMPONENTS_AUTOFILL_CONTENT_RENDERER_TEST_PASSWORD_GENERATION_AGENT_H_
+#define COMPONENTS_AUTOFILL_CONTENT_RENDERER_TEST_PASSWORD_GENERATION_AGENT_H_
+
+#include <vector>
+
+#include "base/memory/scoped_vector.h"
+#include "components/autofill/content/renderer/password_generation_agent.h"
+#include "ipc/ipc_message.h"
+
+namespace autofill {
+
+class TestPasswordGenerationAgent : public PasswordGenerationAgent {
+ public:
+ explicit TestPasswordGenerationAgent(content::RenderView* render_view);
+ virtual ~TestPasswordGenerationAgent();
+
+ // content::RenderViewObserver implementation:
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual bool Send(IPC::Message* message) OVERRIDE;
+
+ // Access messages that would have been sent to the browser.
+ const std::vector<IPC::Message*>& messages() const { return messages_.get(); }
+
+ // Clear outgoing message queue.
+ void clear_messages() { messages_.clear(); }
+
+ // PasswordGenreationAgent implementation:
+ // Always return true to allow loading of data URLs.
+ virtual bool ShouldAnalyzeDocument(
+ const blink::WebDocument& document) const OVERRIDE;
+
+ private:
+ ScopedVector<IPC::Message> messages_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestPasswordGenerationAgent);
+};
+
+} // namespace autofill
+
+#endif // COMPONENTS_AUTOFILL_CONTENT_RENDERER_TEST_PASSWORD_AUTOFILL_AGENT_H_
diff --git a/components/autofill/core/common/autofill_switches.cc b/components/autofill/core/common/autofill_switches.cc
index 8174c03..c7cd3acd 100644
--- a/components/autofill/core/common/autofill_switches.cc
+++ b/components/autofill/core/common/autofill_switches.cc
@@ -31,10 +31,10 @@ const char kEnableInteractiveAutocomplete[] = "enable-interactive-autocomplete";
// account creation.
const char kEnablePasswordGeneration[] = "enable-password-generation";
-// Removes the requirement that we recieved a ping from the autofill servers.
-// Used in testing.
-const char kNoAutofillNecessaryForPasswordGeneration[] =
- "no-autofill-for-password-generation";
+// Removes the requirement that we recieved a ping from the autofill servers
+// and that the user doesn't have the given form blacklisted. Used in testing.
+const char kLocalHeuristicsOnlyForPasswordGeneration[] =
+ "local-heuristics-only-for-password-generation";
// Annotates forms with Autofill field type predictions.
const char kShowAutofillTypePredictions[] = "show-autofill-type-predictions";
diff --git a/components/autofill/core/common/autofill_switches.h b/components/autofill/core/common/autofill_switches.h
index 21c3f6d..ec8c6bb 100644
--- a/components/autofill/core/common/autofill_switches.h
+++ b/components/autofill/core/common/autofill_switches.h
@@ -16,7 +16,7 @@ extern const char kDisablePasswordGeneration[];
extern const char kEnableIgnoreAutocompleteOff[];
extern const char kEnableInteractiveAutocomplete[];
extern const char kEnablePasswordGeneration[];
-extern const char kNoAutofillNecessaryForPasswordGeneration[];
+extern const char kLocalHeuristicsOnlyForPasswordGeneration[];
extern const char kShowAutofillTypePredictions[];
extern const char kWalletSecureServiceUrl[];
extern const char kWalletServiceUrl[];
diff --git a/components/autofill/core/common/password_generation_util.h b/components/autofill/core/common/password_generation_util.h
index 69003c7..daf3c20 100644
--- a/components/autofill/core/common/password_generation_util.h
+++ b/components/autofill/core/common/password_generation_util.h
@@ -9,6 +9,7 @@ namespace autofill {
namespace password_generation {
// Enumerates various events related to the password generation process.
+// Do not remove items from this enum as they are used for UMA stats logging.
enum PasswordGenerationEvent {
// No Account creation form is detected.
NO_SIGN_UP_DETECTED,
@@ -16,11 +17,31 @@ enum PasswordGenerationEvent {
// Account creation form is detected.
SIGN_UP_DETECTED,
- // Password generation icon is shown inside the first password field.
- ICON_SHOWN,
+ // DEPRECATED: Password generation icon shown (old UI).
+ DEPRECATED_ICON_SHOWN,
- // Password generation bubble is shown after user clicks on the icon.
- BUBBLE_SHOWN,
+ // DEPRECATED: Password generation bubble shown (old UI).
+ DEPRECATED_BUBBLE_SHOWN,
+
+ // Password generation could be triggered if the user selects the appropriate
+ // element.
+ GENERATION_AVAILABLE,
+
+ // Password generation popup is shown after user focuses the appropriate
+ // password field.
+ GENERATION_POPUP_SHOWN,
+
+ // Generated password was accepted by the user.
+ PASSWORD_ACCEPTED,
+
+ // User focused the password field containing the generated password.
+ EDITING_POPUP_SHOWN,
+
+ // Password was changed after generation.
+ PASSWORD_EDITED,
+
+ // Generated password was deleted by the user
+ PASSWORD_DELETED,
// Number of enum entries, used for UMA histogram reporting macros.
EVENT_ENUM_COUNT
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index e4515c0..85f75ac 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -12587,6 +12587,19 @@ other types of suffix sets.
</summary>
</histogram>
+<histogram name="PasswordGeneration.Event" enum="PasswordGenerationEvent">
+ <summary>
+ Measures the frequency of various password generation events.
+
+ Note that this histogram is logged from the renderer process, and
+ consequently the numbers should not be directly compared to the other
+ PasswordGeneration.* histograms, which are logged from the browser process.
+ Histograms logged in different processes are lost at different rates, which
+ introduces systematic bias between histograms logged in the renderer process
+ vs. those logged in the browser process.
+ </summary>
+</histogram>
+
<histogram name="PasswordGeneration.UploadStarted" enum="Boolean">
<summary>
The number of times that we try to upload a form that we believe should
@@ -28816,6 +28829,19 @@ other types of suffix sets.
<int value="3" label="Cookie contains both control chars and is invalid"/>
</enum>
+<enum name="PasswordGenerationEvent" type="int">
+ <int value="0" label="No sign up form"/>
+ <int value="1" label="Local heuristics found sign up form"/>
+ <int value="2" label="DEPRECATED: Icon shown"/>
+ <int value="3" label="DEPRECATED: Bubble shown"/>
+ <int value="4" label="Generation available"/>
+ <int value="5" label="Generation popup shown"/>
+ <int value="6" label="Generated password accepted"/>
+ <int value="7" label="Editing popup shown"/>
+ <int value="8" label="Generated password edited"/>
+ <int value="9" label="Generated password deleted"/>
+</enum>
+
<enum name="PasswordManagerActionsTaken" type="int">
<obsolete>
Deprecated as of Chrome 32. See PasswordManagerActionsTakenWithPsl