#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"
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() {}
// 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()))
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;
}
rect,
element.maxLength(),
*password_form));
- password_generation::LogPasswordGenerationEvent(
- password_generation::BUBBLE_SHOWN);
}
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
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) {
+ if (!generation_element_.isNull())
+ generation_element_.setShouldRevealPassword(false);
+
+ 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_) {
+ generation_element_.setShouldRevealPassword(true);
+ ShowEditingPopup();
}
- passwords_[0].passwordGeneratorButtonElement().setAttribute("style",
- "display:block");
+ // 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);
+ }
+
+ // Do not treat the password as generated.
+ // TODO(gcasto): Set PasswordForm::type in the browser to TYPE_NORMAL.
+ password_is_generated_ = false;
+ generation_element_.setShouldRevealPassword(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());
+ }
+ }
+
+ 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,
+ *possible_account_creation_form_));
+
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::EDITING_POPUP_SHOWN);
+}
+
+void PasswordGenerationAgent::HidePopup() {
+ Send(new AutofillHostMsg_HidePasswordGenerationPopup(routing_id()));
}
} // namespace autofill