This is the cherry pick merge for the M73 branch which was approved here: http://crbug.com/927204

[Autofill Assistant] Prompt only shows button if element exists

Before this patch, buttons were always displayed on prompt. That was
incomplete: it did not allow reproducing the behavior we currently have
between scripts where button appear as a result of DOM changes.

With this patch, prompt button reproduce the between-script behavior,
allowing buttons to be shown only conditionally.

Bug: 122825639
Change-Id: I53eee48156cac1f2e5f1b81dd86a48bb25566a0c
Reviewed-on: https://chromium-review.googlesource.com/c/1437084
Commit-Queue: Stephane Zermatten <szermatt@chromium.org>
Reviewed-by: Mathias Carlen <mcarlen@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#626943}(cherry picked from commit 97ac6fc29d0d6832c7329cdb103dfd74f4aee861)
Reviewed-on: https://chromium-review.googlesource.com/c/1448465
Reviewed-by: Stephane Zermatten <szermatt@chromium.org>
Cr-Commit-Position: refs/branch-heads/3683@{#109}
Cr-Branched-From: e51029943e0a38dd794b73caaf6373d5496ae783-refs/heads/master@{#625896}
diff --git a/components/autofill_assistant/browser/actions/prompt_action.cc b/components/autofill_assistant/browser/actions/prompt_action.cc
index 17d1041..f3a83b48 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.cc
+++ b/components/autofill_assistant/browser/actions/prompt_action.cc
@@ -27,9 +27,52 @@
   delegate->SetStatusMessage(proto_.prompt().message());
 
   callback_ = std::move(callback);
-  auto chips = std::make_unique<std::vector<Chip>>();
   DCHECK_GT(proto_.prompt().choices_size(), 0);
 
+  delegate->Prompt(CreateChips());
+
+  batch_element_checker_ = delegate->CreateBatchElementChecker();
+
+  // Register elements whose existence enable new chips.
+  for (int i = 0; i < proto_.prompt().choices_size(); i++) {
+    auto& choice_proto = proto_.prompt().choices(i);
+    Selector selector(choice_proto.show_only_if_element_exists());
+    if (selector.empty())
+      continue;
+
+    batch_element_checker_->AddElementCheck(
+        kExistenceCheck, selector,
+        base::BindOnce(&PromptAction::OnRequiredElementExists,
+                       weak_ptr_factory_.GetWeakPtr(),
+                       base::Unretained(delegate), i));
+  }
+
+  // Wait as long as necessary for one of the elements to show up. This is
+  // cancelled by CancelProto()
+  for (const auto& choice_proto : proto_.prompt().choices()) {
+    Selector selector(choice_proto.element_exists());
+    if (selector.empty())
+      continue;
+
+    std::string payload;
+    choice_proto.SerializeToString(&payload);
+    batch_element_checker_->AddElementCheck(
+        kExistenceCheck, selector,
+        base::BindOnce(&PromptAction::OnElementExist,
+                       weak_ptr_factory_.GetWeakPtr(), payload));
+  }
+
+  batch_element_checker_->Run(
+      base::TimeDelta::Max(),
+      /* try_done= */
+      base::BindRepeating(&PromptAction::OnElementChecksDone,
+                          weak_ptr_factory_.GetWeakPtr(),
+                          base::Unretained(delegate)),
+      /* all_done= */ base::DoNothing());
+}
+
+std::unique_ptr<std::vector<Chip>> PromptAction::CreateChips() {
+  auto chips = std::make_unique<std::vector<Chip>>();
   // TODO(crbug.com/806868): Surface type in proto instead of guessing it from
   // highlight flag.
   Chip::Type non_highlight_type = Chip::Type::CHIP_ASSISTIVE;
@@ -40,8 +83,10 @@
     }
   }
 
-  for (const auto& choice_proto : proto_.prompt().choices()) {
-    if (choice_proto.name().empty())
+  for (int i = 0; i < proto_.prompt().choices_size(); i++) {
+    auto& choice_proto = proto_.prompt().choices(i);
+    if (choice_proto.show_only_if_element_exists().selectors_size() > 0 &&
+        required_element_found_.count(i) == 0)
       continue;
 
     chips->emplace_back();
@@ -57,29 +102,7 @@
         base::BindOnce(&PromptAction::OnSuggestionChosen,
                        weak_ptr_factory_.GetWeakPtr(), server_payload);
   }
-  delegate->Prompt(std::move(chips));
-
-  batch_element_checker_ = delegate->CreateBatchElementChecker();
-  for (const auto& choice_proto : proto_.prompt().choices()) {
-    if (choice_proto.element_exists().selectors_size() == 0)
-      continue;
-
-    std::string payload;
-    choice_proto.SerializeToString(&payload);
-    batch_element_checker_->AddElementCheck(
-        kExistenceCheck, Selector(choice_proto.element_exists()),
-        base::BindOnce(&PromptAction::OnElementExist,
-                       weak_ptr_factory_.GetWeakPtr(), payload));
-  }
-  // Wait as long as necessary for one of the elements to show up. This is
-  // cancelled by OnSuggestionChosen()
-  batch_element_checker_->Run(
-      base::TimeDelta::Max(),
-      /* try_done= */
-      base::BindRepeating(&PromptAction::OnElementChecksDone,
-                          weak_ptr_factory_.GetWeakPtr(),
-                          base::Unretained(delegate)),
-      /* all_done= */ base::DoNothing());
+  return chips;
 }
 
 void PromptAction::OnElementExist(const std::string& payload, bool exists) {
@@ -91,6 +114,16 @@
   // callback.
 }
 
+void PromptAction::OnRequiredElementExists(ActionDelegate* delegate,
+                                           int choice_index,
+                                           bool exists) {
+  if (!exists)
+    return;
+
+  required_element_found_.insert(choice_index);
+  delegate->Prompt(CreateChips());
+}
+
 void PromptAction::OnElementChecksDone(ActionDelegate* delegate) {
   if (!forced_payload_.empty()) {
     delegate->CancelPrompt();
diff --git a/components/autofill_assistant/browser/actions/prompt_action.h b/components/autofill_assistant/browser/actions/prompt_action.h
index 66dd536..49f8192f 100644
--- a/components/autofill_assistant/browser/actions/prompt_action.h
+++ b/components/autofill_assistant/browser/actions/prompt_action.h
@@ -6,12 +6,15 @@
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_PROMPT_ACTION_H_
 
 #include <memory>
+#include <set>
 #include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/autofill_assistant/browser/actions/action.h"
 #include "components/autofill_assistant/browser/batch_element_checker.h"
+#include "components/autofill_assistant/browser/chip.h"
 
 namespace autofill_assistant {
 
@@ -26,7 +29,11 @@
   void InternalProcessAction(ActionDelegate* delegate,
                              ProcessActionCallback callback) override;
 
+  std::unique_ptr<std::vector<Chip>> CreateChips();
   void OnElementExist(const std::string& payload, bool exists);
+  void OnRequiredElementExists(ActionDelegate* delegate,
+                               int choice_index,
+                               bool exists);
   void OnElementChecksDone(ActionDelegate* delegate);
   void OnSuggestionChosen(const std::string& payload);
 
@@ -34,6 +41,10 @@
 
   std::string forced_payload_;
   std::unique_ptr<BatchElementChecker> batch_element_checker_;
+
+  // Index of an element in PromptActionProto::choices that has a
+  // show_only_if_element_exists condition that is met.
+  std::set<int> required_element_found_;
   base::WeakPtrFactory<PromptAction> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(PromptAction);
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index e0aedf5..843a939 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -564,6 +564,9 @@
     // Server payload originally sent by the server. This should
     // be transmitted as-is by the client without interpreting.
     optional bytes server_payload = 5;
+
+    // The chip is only visible if the given element exists.
+    optional ElementReferenceProto show_only_if_element_exists = 6;
   }
   repeated Choice choices = 4;
 }