#include "components/autofill/core/common/password_generation_util.h"
#include "content/public/renderer/render_view.h"
#include "google_apis/gaia/gaia_urls.h"
-#include "third_party/WebKit/public/platform/WebCString.h"
-#include "third_party/WebKit/public/platform/WebRect.h"
#include "third_party/WebKit/public/platform/WebVector.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebFormElement.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
#include "third_party/WebKit/public/web/WebInputElement.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
#include "third_party/WebKit/public/web/WebView.h"
#include "ui/gfx/rect.h"
return false;
}
+void CopyValueToAllInputElements(
+ const blink::WebString value,
+ std::vector<blink::WebInputElement>* elements) {
+ for (std::vector<blink::WebInputElement>::iterator it = elements->begin();
+ it != elements->end(); ++it) {
+ it->setValue(value, true /* sendEvents */);
+ }
+}
+
} // namespace
PasswordGenerationAgent::PasswordGenerationAgent(
render_view_(render_view),
password_is_generated_(false),
password_edited_(false),
+ generation_popup_shown_(false),
+ editing_popup_shown_(false),
enabled_(password_generation::IsPasswordGenerationEnabled()) {
DVLOG(2) << "Password Generation is " << (enabled_ ? "Enabled" : "Disabled");
}
PasswordGenerationAgent::~PasswordGenerationAgent() {}
-void PasswordGenerationAgent::DidFinishDocumentLoad(blink::WebFrame* frame) {
+void PasswordGenerationAgent::DidFinishDocumentLoad(
+ blink::WebLocalFrame* frame) {
// In every navigation, the IPC message sent by the password autofill manager
// 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
generation_enabled_forms_.clear();
generation_element_.reset();
possible_account_creation_form_.reset(new PasswordForm());
+
+ // Log statistics after navigation so that we only log once per page.
+ if (password_elements_.empty()) {
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::NO_SIGN_UP_DETECTED);
+ } else {
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::SIGN_UP_DETECTED);
+ }
password_elements_.clear();
password_is_generated_ = false;
if (password_edited_) {
password_generation::PASSWORD_EDITED);
}
password_edited_ = false;
+
+ if (generation_popup_shown_) {
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::GENERATION_POPUP_SHOWN);
+ }
+ generation_popup_shown_ = false;
+
+ if (editing_popup_shown_) {
+ password_generation::LogPasswordGenerationEvent(
+ password_generation::EDITING_POPUP_SHOWN);
+ }
+ editing_popup_shown_ = false;
}
}
-void PasswordGenerationAgent::DidFinishLoad(blink::WebFrame* frame) {
+void PasswordGenerationAgent::OnDynamicFormsSeen(blink::WebLocalFrame* frame) {
+ FindPossibleGenerationForm(frame);
+}
+
+void PasswordGenerationAgent::DidFinishLoad(blink::WebLocalFrame* frame) {
+ FindPossibleGenerationForm(frame);
+}
+
+void PasswordGenerationAgent::FindPossibleGenerationForm(
+ blink::WebLocalFrame* frame) {
if (!enabled_)
return;
if (!ShouldAnalyzeDocument(frame->document()))
return;
+ // If we have already found a signup form for this page, no need to continue.
+ if (!password_elements_.empty())
+ return;
+
blink::WebVector<blink::WebFormElement> forms;
frame->document().forms(forms);
for (size_t i = 0; i < forms.size(); ++i) {
std::vector<blink::WebInputElement> passwords;
if (GetAccountCreationPasswordFields(forms[i], &passwords)) {
DVLOG(2) << "Account creation form detected";
- password_generation::LogPasswordGenerationEvent(
- password_generation::SIGN_UP_DETECTED);
password_elements_ = passwords;
possible_account_creation_form_.swap(password_form);
DetermineGenerationElement();
return;
}
}
- password_generation::LogPasswordGenerationEvent(
- password_generation::NO_SIGN_UP_DETECTED);
}
bool PasswordGenerationAgent::ShouldAnalyzeDocument(
for (std::vector<blink::WebInputElement>::iterator it =
password_elements_.begin();
it != password_elements_.end(); ++it) {
- it->setValue(password);
+ it->setValue(password, true /* sendEvents */);
it->setAutofilled(true);
// Advance focus to the next input field. We assume password fields in
// an account creation form are always adjacent.
password_generation::GENERATION_AVAILABLE);
}
-void PasswordGenerationAgent::FocusedNodeChanged(const blink::WebNode& node) {
+bool PasswordGenerationAgent::FocusedNodeHasChanged(
+ const blink::WebNode& node) {
if (!generation_element_.isNull())
generation_element_.setShouldRevealPassword(false);
if (node.isNull() || !node.isElementNode())
- return;
+ return false;
const blink::WebElement web_element = node.toConst<blink::WebElement>();
if (!web_element.document().frame())
- return;
+ return false;
const blink::WebInputElement* element = toWebInputElement(&web_element);
if (!element || *element != generation_element_)
- return;
+ return false;
if (password_is_generated_) {
generation_element_.setShouldRevealPassword(true);
ShowEditingPopup();
+ return true;
}
- // Only trigger if the password field is empty.
+ // Assume that if the password field has less than kMaximumOfferSize
+ // characters then the user is not finished typing their password and display
+ // the password suggestion.
if (!element->isReadOnly() &&
element->isEnabled() &&
- element->value().isEmpty()) {
+ element->value().length() <= kMaximumOfferSize) {
ShowGenerationPopup();
+ return true;
}
+
+ return false;
}
bool PasswordGenerationAgent::TextDidChangeInTextField(
// User generated a password and then deleted it.
password_generation::LogPasswordGenerationEvent(
password_generation::PASSWORD_DELETED);
+ CopyValueToAllInputElements(element.value(), &password_elements_);
}
// Do not treat the password as generated.
// Offer generation again.
ShowGenerationPopup();
- } else if (!password_is_generated_) {
+ } else if (password_is_generated_) {
+ password_edited_ = true;
+ // Mirror edits to any confirmation password fields.
+ CopyValueToAllInputElements(element.value(), &password_elements_);
+ } else if (element.value().length() > kMaximumOfferSize) {
// 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());
- }
+ // Password isn't generated and there are fewer than kMaximumOfferSize
+ // characters typed, so keep offering the password. Note this function
+ // will just keep the previous popup if one is already showing.
+ ShowGenerationPopup();
}
return true;
generation_element_.maxLength(),
*possible_account_creation_form_));
- password_generation::LogPasswordGenerationEvent(
- password_generation::GENERATION_POPUP_SHOWN);
+ generation_popup_shown_ = true;
}
void PasswordGenerationAgent::ShowEditingPopup() {
bounding_box_scaled,
*possible_account_creation_form_));
- password_generation::LogPasswordGenerationEvent(
- password_generation::EDITING_POPUP_SHOWN);
+ editing_popup_shown_ = true;
}
void PasswordGenerationAgent::HidePopup() {