| Index: components/autofill/content/renderer/password_autofill_agent.cc
 | 
| diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
 | 
| index e73ef7c656ae558b3f756e921fac3fe9a79c704c..6ea1c0cc7fc98171d9d70fb1284e8877437d7150 100644
 | 
| --- a/components/autofill/content/renderer/password_autofill_agent.cc
 | 
| +++ b/components/autofill/content/renderer/password_autofill_agent.cc
 | 
| @@ -44,6 +44,7 @@
 | 
|  #include "third_party/WebKit/public/web/WebDocument.h"
 | 
|  #include "third_party/WebKit/public/web/WebElement.h"
 | 
|  #include "third_party/WebKit/public/web/WebFormElement.h"
 | 
| +#include "third_party/WebKit/public/web/WebFormElementObserverCallback.h"
 | 
|  #include "third_party/WebKit/public/web/WebLocalFrame.h"
 | 
|  #include "third_party/WebKit/public/web/WebNode.h"
 | 
|  #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
 | 
| @@ -618,6 +619,34 @@ bool HasPasswordField(const blink::WebLocalFrame& frame) {
 | 
|  
 | 
|  }  // namespace
 | 
|  
 | 
| +class PasswordAutofillAgent::FormElementObserver
 | 
| +    : public blink::WebFormElementObserverCallback {
 | 
| + public:
 | 
| +  explicit FormElementObserver(PasswordAutofillAgent* agent) : agent_(agent) {}
 | 
| +  ~FormElementObserver() override { Detach(); }
 | 
| +
 | 
| +  void Detach() {
 | 
| +    if (agent_) {
 | 
| +      DCHECK_EQ(agent_->form_element_observer_, this);
 | 
| +      agent_->form_element_observer_ = nullptr;
 | 
| +    }
 | 
| +    agent_ = nullptr;
 | 
| +  }
 | 
| +
 | 
| +  void ElementWasHiddenOrRemoved() override {
 | 
| +    if (!agent_)
 | 
| +      return;
 | 
| +    agent_->OnSameDocumentNavigationCompleted(
 | 
| +        PasswordForm::SubmissionIndicatorEvent::DOM_MUTATION_AFTER_XHR);
 | 
| +    // The above call will delete "this".
 | 
| +  }
 | 
| +
 | 
| + private:
 | 
| +  PasswordAutofillAgent* agent_;
 | 
| +
 | 
| +  DISALLOW_COPY_AND_ASSIGN(FormElementObserver);
 | 
| +};
 | 
| +
 | 
|  ////////////////////////////////////////////////////////////////////////////////
 | 
|  // PasswordAutofillAgent, public:
 | 
|  
 | 
| @@ -628,13 +657,16 @@ PasswordAutofillAgent::PasswordAutofillAgent(content::RenderFrame* render_frame)
 | 
|        was_password_autofilled_(false),
 | 
|        sent_request_to_store_(false),
 | 
|        checked_safe_browsing_reputation_(false),
 | 
| -      binding_(this) {
 | 
| +      binding_(this),
 | 
| +      form_element_observer_(nullptr) {
 | 
|    // PasswordAutofillAgent is guaranteed to outlive |render_frame|.
 | 
|    render_frame->GetInterfaceRegistry()->AddInterface(
 | 
|        base::Bind(&PasswordAutofillAgent::BindRequest, base::Unretained(this)));
 | 
|  }
 | 
|  
 | 
|  PasswordAutofillAgent::~PasswordAutofillAgent() {
 | 
| +  if (form_element_observer_)
 | 
| +    form_element_observer_->Detach();
 | 
|  }
 | 
|  
 | 
|  void PasswordAutofillAgent::BindRequest(
 | 
| @@ -1032,37 +1064,53 @@ void PasswordAutofillAgent::OnDynamicFormsSeen() {
 | 
|  }
 | 
|  
 | 
|  void PasswordAutofillAgent::AJAXSucceeded() {
 | 
| -  OnSameDocumentNavigationCompleted(false);
 | 
| +  OnSameDocumentNavigationCompleted(
 | 
| +      PasswordForm::SubmissionIndicatorEvent::XHR_SUCCEEDED);
 | 
|  }
 | 
|  
 | 
|  void PasswordAutofillAgent::OnSameDocumentNavigationCompleted(
 | 
| -    bool is_inpage_navigation) {
 | 
| +    PasswordForm::SubmissionIndicatorEvent event) {
 | 
|    if (!provisionally_saved_form_.IsPasswordValid())
 | 
|      return;
 | 
|  
 | 
| -  provisionally_saved_form_.SetSubmissionIndicatorEvent(
 | 
| -      is_inpage_navigation
 | 
| -          ? PasswordForm::SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION
 | 
| -          : PasswordForm::SubmissionIndicatorEvent::XHR_SUCCEEDED);
 | 
| -
 | 
|    // Prompt to save only if the form is now gone, either invisible or
 | 
|    // removed from the DOM.
 | 
|    blink::WebFrame* frame = render_frame()->GetWebFrame();
 | 
|    const auto& password_form = provisionally_saved_form_.password_form();
 | 
|    // TODO(crbug.com/720347): This method could be called often and checking form
 | 
|    // visibility could be expesive. Add performance metrics for this.
 | 
| -  if (form_util::IsFormVisible(frame, provisionally_saved_form_.form_element(),
 | 
| -                               password_form.action, password_form.origin,
 | 
| -                               password_form.form_data) ||
 | 
| -      (provisionally_saved_form_.form_element().IsNull() &&
 | 
| -       IsUnownedPasswordFormVisible(
 | 
| -           frame, provisionally_saved_form_.input_element(),
 | 
| -           password_form.action, password_form.origin, password_form.form_data,
 | 
| -           form_predictions_))) {
 | 
| +  if (event != PasswordForm::SubmissionIndicatorEvent::DOM_MUTATION_AFTER_XHR &&
 | 
| +      (form_util::IsFormVisible(frame, provisionally_saved_form_.form_element(),
 | 
| +                                password_form.action, password_form.origin,
 | 
| +                                password_form.form_data) ||
 | 
| +       (provisionally_saved_form_.form_element().IsNull() &&
 | 
| +        IsUnownedPasswordFormVisible(
 | 
| +            frame, provisionally_saved_form_.input_element(),
 | 
| +            password_form.action, password_form.origin, password_form.form_data,
 | 
| +            form_predictions_)))) {
 | 
| +    if (!form_element_observer_) {
 | 
| +      std::unique_ptr<FormElementObserver> observer(
 | 
| +          new FormElementObserver(this));
 | 
| +      form_element_observer_ = observer.get();
 | 
| +      if (provisionally_saved_form_.form_element().IsNull()) {
 | 
| +        provisionally_saved_form_.input_element()
 | 
| +            .GetDocument()
 | 
| +            .ObserveFormElement(provisionally_saved_form_.input_element(),
 | 
| +                                std::move(observer));
 | 
| +      } else {
 | 
| +        provisionally_saved_form_.form_element()
 | 
| +            .GetDocument()
 | 
| +            .ObserveFormElement(provisionally_saved_form_.form_element(),
 | 
| +                                std::move(observer));
 | 
| +      }
 | 
| +    }
 | 
|      return;
 | 
|    }
 | 
|  
 | 
| +  provisionally_saved_form_.SetSubmissionIndicatorEvent(event);
 | 
|    GetPasswordManagerDriver()->InPageNavigation(password_form);
 | 
| +  if (form_element_observer_)
 | 
| +    form_element_observer_->Detach();
 | 
|    provisionally_saved_form_.Reset();
 | 
|  }
 | 
|  
 | 
| @@ -1217,7 +1265,8 @@ void PasswordAutofillAgent::DidCommitProvisionalLoad(
 | 
|      bool is_new_navigation,
 | 
|      bool is_same_document_navigation) {
 | 
|    if (is_same_document_navigation) {
 | 
| -    OnSameDocumentNavigationCompleted(true);
 | 
| +    OnSameDocumentNavigationCompleted(
 | 
| +        PasswordForm::SubmissionIndicatorEvent::SAME_DOCUMENT_NAVIGATION);
 | 
|    } else {
 | 
|      checked_safe_browsing_reputation_ = false;
 | 
|    }
 | 
| @@ -1299,6 +1348,8 @@ void PasswordAutofillAgent::WillSubmitForm(const blink::WebFormElement& form) {
 | 
|      // RenderView to be instantiated (such as redirects to the WebStore)
 | 
|      // we will never get to finish the load.
 | 
|      GetPasswordManagerDriver()->PasswordFormSubmitted(*submitted_form);
 | 
| +    if (form_element_observer_)
 | 
| +      form_element_observer_->Detach();
 | 
|      provisionally_saved_form_.Reset();
 | 
|    } else if (logger) {
 | 
|      logger->LogMessage(Logger::STRING_FORM_IS_NOT_PASSWORD);
 | 
| @@ -1346,6 +1397,8 @@ void PasswordAutofillAgent::DidStartProvisionalLoad(
 | 
|        }
 | 
|        GetPasswordManagerDriver()->PasswordFormSubmitted(
 | 
|            provisionally_saved_form_.password_form());
 | 
| +      if (form_element_observer_)
 | 
| +        form_element_observer_->Detach();
 | 
|        provisionally_saved_form_.Reset();
 | 
|      } else {
 | 
|        std::vector<std::unique_ptr<PasswordForm>> possible_submitted_forms;
 | 
| @@ -1629,6 +1682,8 @@ void PasswordAutofillAgent::FrameClosing() {
 | 
|      password_to_username_.erase(iter.second.password_field);
 | 
|    }
 | 
|    web_input_to_password_info_.clear();
 | 
| +  if (form_element_observer_)
 | 
| +    form_element_observer_->Detach();
 | 
|    provisionally_saved_form_.Reset();
 | 
|    field_value_and_properties_map_.clear();
 | 
|    sent_request_to_store_ = false;
 | 
| 
 |