Index: components/autofill/content/renderer/password_generation_agent.cc |
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc |
index da9b8637ff61e94fe98674ee7e3c350dff036f06..f7e01d1ea6ab243b0ff2cf142aa2d1cc14f03e3d 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 |