Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / components / autofill / content / renderer / password_generation_agent.cc
index da9b863..4949f8e 100644 (file)
@@ -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,137 @@ 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) {
+  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