[go: up one dir, main page]

[Autofill Assistant] Make ConfigureBottomSheetAction wait for resize to
happen.

Before this change, ConfigureBottomSheetAction would return immediately
after asking for the viewport to be resized. Depending on how quickly
that happened, a Focus action following ConfigureBottomSheetAction might
see the window height before the resize and not be able to focus.

The current workaround for that is to wait after resizing the viewport
before focusing on an element, but the time it takes varies a lot.

With this change, ConfigureBottomSheetAction waits until the viewport
resize has been seen from the Javascript side, so we don't wait longer
than strictly necessary. A maximum wait time avoids scripts getting
stuck in this action.

This is the M-76 merge for http://crrev/c/1640408.

(cherry picked from commit fc4d56bbaab6747fe94930f83cb4c3e4da02766c)

Bug: 973038
Bug: b/133665079
Change-Id: Ia37076710634542bb4f15c77f30275c234383cb4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1640408
Commit-Queue: Stephane Zermatten <szermatt@chromium.org>
Reviewed-by: Jordan Demeulenaere <jdemeulenaere@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#665557}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1658008
Reviewed-by: Stephane Zermatten <szermatt@chromium.org>
Cr-Commit-Position: refs/branch-heads/3809@{#282}
Cr-Branched-From: d82dec1a818f378c464ba307ddd9c92133eac355-refs/heads/master@{#665002}
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index c770bf6b..c0b1614 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -161,6 +161,7 @@
   testonly = true
   sources = [
     "actions/autofill_action_unittest.cc",
+    "actions/configure_bottom_sheet_action_unittest.cc",
     "actions/mock_action_delegate.cc",
     "actions/mock_action_delegate.h",
     "actions/prompt_action_unittest.cc",
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index 702b3105..0d985f2 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -246,9 +246,19 @@
   // Set whether the viewport should be resized.
   virtual void SetResizeViewport(bool resize_viewport) = 0;
 
+  // Checks whether the viewport should be resized.
+  virtual bool GetResizeViewport() = 0;
+
   // Set the peek mode.
   virtual void SetPeekMode(ConfigureBottomSheetProto::PeekMode peek_mode) = 0;
 
+  // Checks the current peek mode.
+  virtual ConfigureBottomSheetProto::PeekMode GetPeekMode() = 0;
+
+  // Calls the callback once the main document window has been resized.
+  virtual void WaitForWindowHeightChange(
+      base::OnceCallback<void(const ClientStatus&)> callback) = 0;
+
   // Returns the current client settings.
   virtual const ClientSettings& GetSettings() = 0;
 
diff --git a/components/autofill_assistant/browser/actions/configure_bottom_sheet_action.cc b/components/autofill_assistant/browser/actions/configure_bottom_sheet_action.cc
index 447f6c6..d3fd092b 100644
--- a/components/autofill_assistant/browser/actions/configure_bottom_sheet_action.cc
+++ b/components/autofill_assistant/browser/actions/configure_bottom_sheet_action.cc
@@ -4,33 +4,89 @@
 
 #include "components/autofill_assistant/browser/actions/configure_bottom_sheet_action.h"
 
+#include "base/bind.h"
+#include "base/callback.h"
 #include "components/autofill_assistant/browser/actions/action_delegate.h"
+#include "components/autofill_assistant/browser/client_status.h"
 
 namespace autofill_assistant {
 
 ConfigureBottomSheetAction::ConfigureBottomSheetAction(const ActionProto& proto)
-    : Action(proto) {}
+    : Action(proto), weak_ptr_factory_(this) {}
 
 ConfigureBottomSheetAction::~ConfigureBottomSheetAction() {}
 
 void ConfigureBottomSheetAction::InternalProcessAction(
     ActionDelegate* delegate,
     ProcessActionCallback callback) {
-  if (proto_.configure_bottom_sheet().viewport_resizing() ==
-      ConfigureBottomSheetProto::RESIZE) {
+  const ConfigureBottomSheetProto& proto = proto_.configure_bottom_sheet();
+
+  if (proto.resize_timeout_ms() > 0) {
+    // When a window resize is expected, we want to wait for the size change to
+    // be visible to Javascript before moving on to another action. To do that,
+    // this action registers a callback *before* making any change and waits for
+    // a 'resize' event in the Javascript side.
+    bool resize = delegate->GetResizeViewport();
+    bool expect_resize =
+        (!resize &&
+         proto.viewport_resizing() == ConfigureBottomSheetProto::RESIZE) ||
+        (resize &&
+         (proto.viewport_resizing() == ConfigureBottomSheetProto::NO_RESIZE ||
+          (proto.peek_mode() !=
+               ConfigureBottomSheetProto::UNDEFINED_PEEK_MODE &&
+           proto.peek_mode() != delegate->GetPeekMode())));
+    if (expect_resize) {
+      callback_ = std::move(callback);
+
+      timer_.Start(FROM_HERE,
+                   base::TimeDelta::FromMilliseconds(proto.resize_timeout_ms()),
+                   base::BindOnce(&ConfigureBottomSheetAction::OnTimeout,
+                                  weak_ptr_factory_.GetWeakPtr()));
+
+      delegate->WaitForWindowHeightChange(
+          base::BindOnce(&ConfigureBottomSheetAction::OnWindowHeightChange,
+                         weak_ptr_factory_.GetWeakPtr()));
+    }
+  }
+
+  if (proto.viewport_resizing() == ConfigureBottomSheetProto::RESIZE) {
     delegate->SetResizeViewport(true);
-  } else if (proto_.configure_bottom_sheet().viewport_resizing() ==
+  } else if (proto.viewport_resizing() ==
              ConfigureBottomSheetProto::NO_RESIZE) {
     delegate->SetResizeViewport(false);
   }
 
-  if (proto_.configure_bottom_sheet().peek_mode() !=
-      ConfigureBottomSheetProto::UNDEFINED_PEEK_MODE) {
-    delegate->SetPeekMode(proto_.configure_bottom_sheet().peek_mode());
+  if (proto.peek_mode() != ConfigureBottomSheetProto::UNDEFINED_PEEK_MODE) {
+    delegate->SetPeekMode(proto.peek_mode());
   }
 
-  UpdateProcessedAction(ACTION_APPLIED);
-  std::move(callback).Run(std::move(processed_action_proto_));
+  if (callback) {
+    UpdateProcessedAction(OkClientStatus());
+    std::move(callback).Run(std::move(processed_action_proto_));
+  }
+}
+
+void ConfigureBottomSheetAction::OnWindowHeightChange(
+    const ClientStatus& status) {
+  if (!callback_)
+    return;
+
+  timer_.Stop();
+  UpdateProcessedAction(status);
+  std::move(callback_).Run(std::move(processed_action_proto_));
+}
+
+void ConfigureBottomSheetAction::OnTimeout() {
+  if (!callback_)
+    return;
+
+  DVLOG(2)
+      << __func__
+      << " Timed out waiting for window height change. Continuing anyways.";
+  UpdateProcessedAction(OkClientStatus());
+  processed_action_proto_->mutable_status_details()->set_original_status(
+      ProcessedActionStatusProto::TIMED_OUT);
+  std::move(callback_).Run(std::move(processed_action_proto_));
 }
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/configure_bottom_sheet_action.h b/components/autofill_assistant/browser/actions/configure_bottom_sheet_action.h
index ec1247812..8637a3c 100644
--- a/components/autofill_assistant/browser/actions/configure_bottom_sheet_action.h
+++ b/components/autofill_assistant/browser/actions/configure_bottom_sheet_action.h
@@ -6,6 +6,8 @@
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_CONFIGURE_BOTTOM_SHEET_ACTION_H_
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/timer/timer.h"
 #include "components/autofill_assistant/browser/actions/action.h"
 
 namespace autofill_assistant {
@@ -21,6 +23,12 @@
   void InternalProcessAction(ActionDelegate* delegate,
                              ProcessActionCallback callback) override;
 
+  void OnWindowHeightChange(const ClientStatus& status);
+  void OnTimeout();
+
+  ProcessActionCallback callback_;
+  base::OneShotTimer timer_;
+  base::WeakPtrFactory<ConfigureBottomSheetAction> weak_ptr_factory_;
   DISALLOW_COPY_AND_ASSIGN(ConfigureBottomSheetAction);
 };
 
diff --git a/components/autofill_assistant/browser/actions/configure_bottom_sheet_action_unittest.cc b/components/autofill_assistant/browser/actions/configure_bottom_sheet_action_unittest.cc
new file mode 100644
index 0000000..b96af79
--- /dev/null
+++ b/components/autofill_assistant/browser/actions/configure_bottom_sheet_action_unittest.cc
@@ -0,0 +1,247 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/autofill_assistant/browser/actions/configure_bottom_sheet_action.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/autofill_assistant/browser/actions/mock_action_delegate.h"
+#include "components/autofill_assistant/browser/mock_run_once_callback.h"
+#include "components/autofill_assistant/browser/mock_web_controller.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace autofill_assistant {
+namespace {
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Invoke;
+using ::testing::IsEmpty;
+using ::testing::IsNull;
+using ::testing::Pointee;
+using ::testing::Property;
+using ::testing::SizeIs;
+
+class ConfigureBottomSheetActionTest : public testing::Test {
+ public:
+  ConfigureBottomSheetActionTest()
+      : task_env_(
+            base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {}
+
+  void SetUp() override {
+    ON_CALL(mock_action_delegate_, GetResizeViewport())
+        .WillByDefault(Invoke([this]() { return resize_viewport_; }));
+    ON_CALL(mock_action_delegate_, SetResizeViewport(_))
+        .WillByDefault(
+            Invoke([this](bool value) { resize_viewport_ = value; }));
+    ON_CALL(mock_action_delegate_, GetPeekMode())
+        .WillByDefault(Invoke([this]() { return peek_mode_; }));
+    ON_CALL(mock_action_delegate_, SetPeekMode(_))
+        .WillByDefault(
+            Invoke([this](ConfigureBottomSheetProto::PeekMode peek_mode) {
+              peek_mode_ = peek_mode;
+            }));
+    ON_CALL(mock_action_delegate_, OnWaitForWindowHeightChange(_))
+        .WillByDefault(Invoke(
+            [this](base::OnceCallback<void(const ClientStatus&)>& callback) {
+              on_resize_cb_ = std::move(callback);
+            }));
+  }
+
+ protected:
+  // Runs the action defined in |proto_| and reports the result to |callback_|.
+  //
+  // Once it has run, the result of the action is available in
+  // |processed_action_|. Before the action has run, |processed_action_| status
+  // is UNKNOWN_ACTION_STATUS.
+  void Run() {
+    ActionProto action_proto;
+    *action_proto.mutable_configure_bottom_sheet() = proto_;
+    action_ = std::make_unique<ConfigureBottomSheetAction>(action_proto);
+    action_->ProcessAction(
+        &mock_action_delegate_,
+        base::BindOnce(base::BindLambdaForTesting(
+            [&](std::unique_ptr<ProcessedActionProto> result) {
+              processed_action_ = *result;
+            })));
+  }
+
+  // Runs an action that waits for a resize.
+  void RunWithTimeout() {
+    proto_.set_resize_timeout_ms(100);
+    Run();
+  }
+
+  // Fast forward time enough for an action created by RunWithTimeout() to time
+  // out.
+  void ForceTimeout() {
+    task_env_.FastForwardBy(base::TimeDelta::FromMilliseconds(100));
+    task_env_.FastForwardBy(base::TimeDelta::FromMilliseconds(100));
+  }
+
+  // task_env_ must be first to guarantee other field
+  // creation run in that environment.
+  base::test::ScopedTaskEnvironment task_env_;
+
+  MockActionDelegate mock_action_delegate_;
+  MockWebController mock_web_controller_;
+  ConfigureBottomSheetProto proto_;
+  bool resize_viewport_ = false;
+  base::OnceCallback<void(const ClientStatus&)> on_resize_cb_;
+  ConfigureBottomSheetProto::PeekMode peek_mode_ =
+      ConfigureBottomSheetProto::HANDLE;
+  std::unique_ptr<ConfigureBottomSheetAction> action_;
+  ProcessedActionProto processed_action_;
+};
+
+TEST_F(ConfigureBottomSheetActionTest, NoOp) {
+  Run();
+
+  EXPECT_EQ(ACTION_APPLIED, processed_action_.status());
+  EXPECT_FALSE(resize_viewport_);
+  EXPECT_EQ(ConfigureBottomSheetProto::HANDLE, peek_mode_);
+}
+
+TEST_F(ConfigureBottomSheetActionTest, ChangePeekMode) {
+  proto_.set_peek_mode(ConfigureBottomSheetProto::HANDLE_HEADER);
+  Run();
+
+  EXPECT_EQ(ACTION_APPLIED, processed_action_.status());
+  EXPECT_FALSE(resize_viewport_);
+  EXPECT_EQ(ConfigureBottomSheetProto::HANDLE_HEADER, peek_mode_);
+}
+
+TEST_F(ConfigureBottomSheetActionTest, EnableResize) {
+  proto_.set_viewport_resizing(ConfigureBottomSheetProto::RESIZE);
+  Run();
+
+  EXPECT_EQ(ACTION_APPLIED, processed_action_.status());
+  EXPECT_TRUE(resize_viewport_);
+  EXPECT_EQ(ConfigureBottomSheetProto::HANDLE, peek_mode_);
+}
+
+TEST_F(ConfigureBottomSheetActionTest, EnableResizeWithPeekMode) {
+  proto_.set_viewport_resizing(ConfigureBottomSheetProto::RESIZE);
+  proto_.set_peek_mode(ConfigureBottomSheetProto::HANDLE_HEADER);
+  Run();
+
+  EXPECT_EQ(ACTION_APPLIED, processed_action_.status());
+  EXPECT_TRUE(resize_viewport_);
+  EXPECT_EQ(ConfigureBottomSheetProto::HANDLE_HEADER, peek_mode_);
+}
+
+TEST_F(ConfigureBottomSheetActionTest, WaitAfterSettingResize) {
+  proto_.set_viewport_resizing(ConfigureBottomSheetProto::RESIZE);
+
+  RunWithTimeout();
+
+  EXPECT_TRUE(resize_viewport_);
+  ASSERT_TRUE(on_resize_cb_);
+
+  std::move(on_resize_cb_).Run(OkClientStatus());
+  EXPECT_EQ(ACTION_APPLIED, processed_action_.status());
+}
+
+TEST_F(ConfigureBottomSheetActionTest, WaitFailsAfterSettingResize) {
+  proto_.set_viewport_resizing(ConfigureBottomSheetProto::RESIZE);
+
+  RunWithTimeout();
+
+  ASSERT_TRUE(on_resize_cb_);
+
+  std::move(on_resize_cb_).Run(ClientStatus(OTHER_ACTION_STATUS));
+
+  EXPECT_EQ(OTHER_ACTION_STATUS, processed_action_.status());
+}
+
+TEST_F(ConfigureBottomSheetActionTest, WaitTimesOut) {
+  proto_.set_viewport_resizing(ConfigureBottomSheetProto::RESIZE);
+
+  RunWithTimeout();
+
+  ASSERT_TRUE(on_resize_cb_);
+
+  ForceTimeout();
+
+  EXPECT_EQ(ACTION_APPLIED, processed_action_.status());
+  EXPECT_EQ(TIMED_OUT, processed_action_.status_details().original_status());
+}
+
+TEST_F(ConfigureBottomSheetActionTest, TimesOutAfterWindowResized) {
+  proto_.set_viewport_resizing(ConfigureBottomSheetProto::RESIZE);
+
+  RunWithTimeout();
+
+  ASSERT_TRUE(on_resize_cb_);
+
+  std::move(on_resize_cb_).Run(OkClientStatus());
+  ForceTimeout();
+
+  EXPECT_EQ(ACTION_APPLIED, processed_action_.status());
+  EXPECT_TRUE(resize_viewport_);
+}
+
+TEST_F(ConfigureBottomSheetActionTest, WindowResizedAfterTimeout) {
+  proto_.set_viewport_resizing(ConfigureBottomSheetProto::RESIZE);
+
+  RunWithTimeout();
+
+  ASSERT_TRUE(on_resize_cb_);
+
+  ForceTimeout();
+  std::move(on_resize_cb_).Run(OkClientStatus());
+  EXPECT_EQ(ACTION_APPLIED, processed_action_.status());
+}
+
+TEST_F(ConfigureBottomSheetActionTest, WaitAfterUnsettingResize) {
+  resize_viewport_ = true;
+  proto_.set_viewport_resizing(ConfigureBottomSheetProto::NO_RESIZE);
+
+  RunWithTimeout();
+
+  ASSERT_TRUE(on_resize_cb_);
+}
+
+TEST_F(ConfigureBottomSheetActionTest, WaitAfterChangingPeekModeInResizeMode) {
+  resize_viewport_ = true;
+  proto_.set_peek_mode(ConfigureBottomSheetProto::HANDLE_HEADER);
+
+  RunWithTimeout();
+
+  EXPECT_EQ(ConfigureBottomSheetProto::HANDLE_HEADER, peek_mode_);
+  ASSERT_TRUE(on_resize_cb_);
+}
+
+TEST_F(ConfigureBottomSheetActionTest, DontWaitAfterChangingPeekIfNoResize) {
+  proto_.set_peek_mode(ConfigureBottomSheetProto::HANDLE_HEADER);
+
+  RunWithTimeout();
+
+  ASSERT_FALSE(on_resize_cb_);
+}
+
+TEST_F(ConfigureBottomSheetActionTest, DontWaitIfPeekModeNotChanged) {
+  resize_viewport_ = true;
+  proto_.set_peek_mode(ConfigureBottomSheetProto::HANDLE);
+
+  RunWithTimeout();
+
+  ASSERT_FALSE(on_resize_cb_);
+}
+
+TEST_F(ConfigureBottomSheetActionTest, DontWaitIfResizeModeNotChanged) {
+  resize_viewport_ = true;
+  proto_.set_viewport_resizing(ConfigureBottomSheetProto::RESIZE);
+
+  RunWithTimeout();
+
+  ASSERT_FALSE(on_resize_cb_);
+}
+
+}  // namespace
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index 79167ab..a446f28b 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -161,13 +161,23 @@
   MOCK_METHOD1(SetProgressVisible, void(bool visible));
   MOCK_METHOD1(SetChips, void(std::unique_ptr<std::vector<Chip>> chips));
   MOCK_METHOD1(SetResizeViewport, void(bool resize_viewport));
+  MOCK_METHOD0(GetResizeViewport, bool());
   MOCK_METHOD1(SetPeekMode,
                void(ConfigureBottomSheetProto::PeekMode peek_mode));
+  MOCK_METHOD0(GetPeekMode, ConfigureBottomSheetProto::PeekMode());
   MOCK_METHOD2(
       SetForm,
       bool(std::unique_ptr<FormProto> form,
            base::RepeatingCallback<void(const FormProto::Result*)> callback));
 
+  void WaitForWindowHeightChange(
+      base::OnceCallback<void(const ClientStatus&)> callback) {
+    OnWaitForWindowHeightChange(callback);
+  }
+
+  MOCK_METHOD1(OnWaitForWindowHeightChange,
+               void(base::OnceCallback<void(const ClientStatus&)>& callback));
+
   const ClientSettings& GetSettings() override { return client_settings_; }
 
   ClientSettings client_settings_;
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
index 1559ec5..ee6b0e7 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.cc
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
@@ -93,10 +93,22 @@
   payment_request_options_ = std::move(options);
 }
 
-void FakeScriptExecutorDelegate::SetResizeViewport(bool resize_viewport) {}
+void FakeScriptExecutorDelegate::SetResizeViewport(bool resize_viewport) {
+  resize_viewport_ = resize_viewport;
+}
+
+bool FakeScriptExecutorDelegate::GetResizeViewport() {
+  return resize_viewport_;
+}
 
 void FakeScriptExecutorDelegate::SetPeekMode(
-    ConfigureBottomSheetProto::PeekMode peek_mode) {}
+    ConfigureBottomSheetProto::PeekMode peek_mode) {
+  peek_mode_ = peek_mode;
+}
+
+ConfigureBottomSheetProto::PeekMode FakeScriptExecutorDelegate::GetPeekMode() {
+  return peek_mode_;
+}
 
 bool FakeScriptExecutorDelegate::HasNavigationError() {
   return navigation_error_;
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.h b/components/autofill_assistant/browser/fake_script_executor_delegate.h
index 671d27d..d9d766f 100644
--- a/components/autofill_assistant/browser/fake_script_executor_delegate.h
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.h
@@ -48,7 +48,9 @@
   void SetPaymentRequestOptions(
       std::unique_ptr<PaymentRequestOptions> options) override;
   void SetResizeViewport(bool resize_viewport) override;
+  bool GetResizeViewport() override;
   void SetPeekMode(ConfigureBottomSheetProto::PeekMode peek_mode) override;
+  ConfigureBottomSheetProto::PeekMode GetPeekMode() override;
   bool SetForm(std::unique_ptr<FormProto> form,
                base::RepeatingCallback<void(const FormProto::Result*)> callback)
       override;
@@ -113,6 +115,9 @@
   bool navigating_to_new_document_ = false;
   bool navigation_error_ = false;
   std::set<ScriptExecutorDelegate::Listener*> listeners_;
+  bool resize_viewport_ = false;
+  ConfigureBottomSheetProto::PeekMode peek_mode_ =
+      ConfigureBottomSheetProto::HANDLE;
 
   DISALLOW_COPY_AND_ASSIGN(FakeScriptExecutorDelegate);
 };
diff --git a/components/autofill_assistant/browser/mock_web_controller.h b/components/autofill_assistant/browser/mock_web_controller.h
index bbfee74a..a725fd8d 100644
--- a/components/autofill_assistant/browser/mock_web_controller.h
+++ b/components/autofill_assistant/browser/mock_web_controller.h
@@ -89,6 +89,14 @@
   }
 
   MOCK_METHOD0(ClearCookie, void());
+
+  void WaitForWindowHeightChange(
+      base::OnceCallback<void(const ClientStatus&)> callback) {
+    OnWaitForWindowHeightChange(callback);
+  }
+
+  MOCK_METHOD1(OnWaitForWindowHeightChange,
+               void(base::OnceCallback<void(const ClientStatus&)>& callback));
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 06cee7c..678f3dd 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -456,11 +456,24 @@
   delegate_->SetResizeViewport(resize_viewport);
 }
 
+bool ScriptExecutor::GetResizeViewport() {
+  return delegate_->GetResizeViewport();
+}
+
 void ScriptExecutor::SetPeekMode(
     ConfigureBottomSheetProto::PeekMode peek_mode) {
   delegate_->SetPeekMode(peek_mode);
 }
 
+ConfigureBottomSheetProto::PeekMode ScriptExecutor::GetPeekMode() {
+  return delegate_->GetPeekMode();
+}
+
+void ScriptExecutor::WaitForWindowHeightChange(
+    base::OnceCallback<void(const ClientStatus&)> callback) {
+  delegate_->GetWebController()->WaitForWindowHeightChange(std::move(callback));
+}
+
 const ClientSettings& ScriptExecutor::GetSettings() {
   return delegate_->GetSettings();
 }
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index 0325853..f3bcedb2 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -179,7 +179,11 @@
   void SetProgress(int progress) override;
   void SetProgressVisible(bool visible) override;
   void SetResizeViewport(bool resize_viewport) override;
+  bool GetResizeViewport() override;
   void SetPeekMode(ConfigureBottomSheetProto::PeekMode peek_mode) override;
+  ConfigureBottomSheetProto::PeekMode GetPeekMode() override;
+  void WaitForWindowHeightChange(
+      base::OnceCallback<void(const ClientStatus&)> callback) override;
   const ClientSettings& GetSettings() override;
   bool SetForm(std::unique_ptr<FormProto> form,
                base::RepeatingCallback<void(const FormProto::Result*)> callback)
diff --git a/components/autofill_assistant/browser/script_executor_delegate.h b/components/autofill_assistant/browser/script_executor_delegate.h
index 7da41dd..70c8c6b 100644
--- a/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_delegate.h
@@ -68,8 +68,10 @@
   virtual void SetProgress(int progress) = 0;
   virtual void SetProgressVisible(bool visible) = 0;
   virtual void SetChips(std::unique_ptr<std::vector<Chip>> chips) = 0;
+  virtual bool GetResizeViewport() = 0;
   virtual void SetResizeViewport(bool resize_viewport) = 0;
   virtual void SetPeekMode(ConfigureBottomSheetProto::PeekMode peek_mode) = 0;
+  virtual ConfigureBottomSheetProto::PeekMode GetPeekMode() = 0;
   virtual bool SetForm(
       std::unique_ptr<FormProto> form,
       base::RepeatingCallback<void(const FormProto::Result*)> callback) = 0;
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 0d703458..e2cf26c1 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -1231,6 +1231,10 @@
   // resize_viewport is true or was set to true by a previous actions, the
   // viewport will be resized to match the new peek height.
   optional PeekMode peek_mode = 2;
+
+  // Maximum time to wait for the window to resize before continuing with the
+  // script. If 0 or unset, the action doesn't wait.
+  optional int32 resize_timeout_ms = 3;
 }
 
 // Allow scripts to display a form with multiple inputs.
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index 458b90b..9f9963b 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -23,6 +23,7 @@
 #include "components/autofill/core/common/autofill_constants.h"
 #include "components/autofill/core/common/form_data.h"
 #include "components/autofill_assistant/browser/client_settings.h"
+#include "components/autofill_assistant/browser/client_status.h"
 #include "components/autofill_assistant/browser/rectf.h"
 #include "components/autofill_assistant/browser/string_conversions_util.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -197,6 +198,26 @@
       selector.click();
     })";
 
+// Javascript code that returns a promise that will succeed once the main
+// document window has changed height.
+//
+// This ignores width changes, to filter out resizes caused by changes to the
+// screen orientation.
+const char* const kWaitForWindowHeightChange = R"(
+new Promise((fulfill, reject) => {
+  var lastWidth = window.innerWidth;
+  var handler = function(event) {
+    if (window.innerWidth != lastWidth) {
+      lastWidth = window.innerWidth;
+      return
+    }
+    window.removeEventListener('resize', handler)
+    fulfill(true)
+  }
+  window.addEventListener('resize', handler)
+})
+)";
+
 bool ConvertPseudoType(const PseudoType pseudo_type,
                        dom::PseudoType* pseudo_type_output) {
   switch (pseudo_type) {
@@ -1149,6 +1170,24 @@
   std::move(callback).Run(status.ok());
 }
 
+void WebController::WaitForWindowHeightChange(
+    base::OnceCallback<void(const ClientStatus&)> callback) {
+  devtools_client_->GetRuntime()->Evaluate(
+      runtime::EvaluateParams::Builder()
+          .SetExpression(kWaitForWindowHeightChange)
+          .SetAwaitPromise(true)
+          .Build(),
+      base::BindOnce(&WebController::OnWaitForWindowHeightChange,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
+void WebController::OnWaitForWindowHeightChange(
+    base::OnceCallback<void(const ClientStatus&)> callback,
+    std::unique_ptr<runtime::EvaluateResult> result) {
+  std::move(callback).Run(
+      CheckJavaScriptResult(result.get(), __FILE__, __LINE__));
+}
+
 void WebController::FindElement(const Selector& selector,
                                 bool strict_mode,
                                 FindElementCallback callback) {
diff --git a/components/autofill_assistant/browser/web_controller.h b/components/autofill_assistant/browser/web_controller.h
index 2c34de9..e63a379 100644
--- a/components/autofill_assistant/browser/web_controller.h
+++ b/components/autofill_assistant/browser/web_controller.h
@@ -186,6 +186,10 @@
                             bool strict,
                             base::OnceCallback<void(bool)> callback);
 
+  // Calls the callback once the main document window has been resized.
+  virtual void WaitForWindowHeightChange(
+      base::OnceCallback<void(const ClientStatus&)> callback);
+
  private:
   friend class WebControllerBrowserTest;
 
@@ -290,6 +294,9 @@
   void OnFindElementForCheck(base::OnceCallback<void(bool)> callback,
                              const ClientStatus& status,
                              std::unique_ptr<FindElementResult> result);
+  void OnWaitForWindowHeightChange(
+      base::OnceCallback<void(const ClientStatus&)> callback,
+      std::unique_ptr<runtime::EvaluateResult> result);
 
   // Find the element given by |selector|. If multiple elements match
   // |selector| and if |strict_mode| is false, return the first one that is
diff --git a/components/autofill_assistant/browser/web_controller_browsertest.cc b/components/autofill_assistant/browser/web_controller_browsertest.cc
index 4253e4e..e123e5aae 100644
--- a/components/autofill_assistant/browser/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web_controller_browsertest.cc
@@ -170,14 +170,14 @@
     ClientStatus result;
     web_controller_->SelectOption(
         selector, option,
-        base::BindOnce(&WebControllerBrowserTest::OnSelectOption,
+        base::BindOnce(&WebControllerBrowserTest::OnClientStatus,
                        base::Unretained(this), run_loop.QuitClosure(),
                        &result));
     run_loop.Run();
     return result;
   }
 
-  void OnSelectOption(base::Closure done_callback,
+  void OnClientStatus(base::Closure done_callback,
                       ClientStatus* result_output,
                       const ClientStatus& status) {
     *result_output = status;
@@ -188,20 +188,13 @@
     base::RunLoop run_loop;
     ClientStatus result;
     web_controller_->HighlightElement(
-        selector, base::BindOnce(&WebControllerBrowserTest::OnHighlightElement,
+        selector, base::BindOnce(&WebControllerBrowserTest::OnClientStatus,
                                  base::Unretained(this), run_loop.QuitClosure(),
                                  &result));
     run_loop.Run();
     return result;
   }
 
-  void OnHighlightElement(base::Closure done_callback,
-                          ClientStatus* result_output,
-                          const ClientStatus& status) {
-    *result_output = status;
-    std::move(done_callback).Run();
-  }
-
   ClientStatus GetOuterHtml(const Selector& selector,
                             std::string* html_output) {
     base::RunLoop run_loop;
@@ -1089,4 +1082,17 @@
   EXPECT_TRUE(SetCookie());
 }
 
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, WaitForHeightChange) {
+  base::RunLoop run_loop;
+  ClientStatus result;
+  web_controller_->WaitForWindowHeightChange(
+      base::BindOnce(&WebControllerBrowserTest::OnClientStatus,
+                     base::Unretained(this), run_loop.QuitClosure(), &result));
+
+  EXPECT_TRUE(
+      content::ExecJs(shell(), "window.dispatchEvent(new Event('resize'))"));
+  run_loop.Run();
+  EXPECT_EQ(ACTION_APPLIED, result.proto_status());
+}
+
 }  // namespace