[go: up one dir, main page]

[Autofill Assistant] Allow setting top padding for FocusAction.

The top padding can be specified either by the Pixels or ratio
relative to the height of the window.

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

(cherry picked from commit e0949be5a94d0ad61a816563160feb94c638e9c4)

Bug: 973043
Bug: 806868
Bug: b/132758584
Change-Id: I2d3f77b04a9ca319634fe2ec1325d24c0052d100
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1631607
Reviewed-by: Stephane Zermatten <szermatt@chromium.org>
Commit-Queue: Lukasz Suder <lsuder@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#665878}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1657896
Reviewed-by: Lukasz Suder <lsuder@chromium.org>
Cr-Commit-Position: refs/branch-heads/3809@{#283}
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 c0b1614..855e5a3 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -131,6 +131,8 @@
     "state.h",
     "string_conversions_util.cc",
     "string_conversions_util.h",
+    "top_padding.cc",
+    "top_padding.h",
     "trigger_context.cc",
     "trigger_context.h",
     "ui_controller.cc",
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index 0d985f2..a0eec45 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -15,6 +15,7 @@
 #include "components/autofill_assistant/browser/details.h"
 #include "components/autofill_assistant/browser/info_box.h"
 #include "components/autofill_assistant/browser/selector.h"
+#include "components/autofill_assistant/browser/top_padding.h"
 #include "components/autofill_assistant/browser/ui_controller.h"
 #include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
 #include "third_party/icu/source/common/unicode/umachine.h"
@@ -129,9 +130,11 @@
       const std::string& selected_option,
       base::OnceCallback<void(const ClientStatus&)> callback) = 0;
 
-  // Focus on the element given by |selector|.
+  // Focus on element given by |selector|. |top_padding| specifies the padding
+  // between focused element and the top.
   virtual void FocusElement(
       const Selector& selector,
+      const TopPadding& top_padding,
       base::OnceCallback<void(const ClientStatus&)> callback) = 0;
 
   // Sets selector of areas that can be manipulated:
diff --git a/components/autofill_assistant/browser/actions/focus_element_action.cc b/components/autofill_assistant/browser/actions/focus_element_action.cc
index ca8c8819..5792e60 100644
--- a/components/autofill_assistant/browser/actions/focus_element_action.cc
+++ b/components/autofill_assistant/browser/actions/focus_element_action.cc
@@ -34,16 +34,35 @@
     std::move(callback).Run(std::move(processed_action_proto_));
     return;
   }
+
+  // Default value of 25%. This value should always be overriden
+  // by backend.
+  TopPadding top_padding{0.25, TopPadding::Unit::RATIO};
+  switch (focus_element.top_padding().top_padding_case()) {
+    case FocusElementProto::TopPadding::kPixels:
+      top_padding = TopPadding(focus_element.top_padding().pixels(),
+                               TopPadding::Unit::PIXELS);
+      break;
+    case FocusElementProto::TopPadding::kRatio:
+      top_padding = TopPadding(focus_element.top_padding().ratio(),
+                               TopPadding::Unit::RATIO);
+      break;
+    case FocusElementProto::TopPadding::TOP_PADDING_NOT_SET:
+      // Default value set before switch.
+      break;
+  }
+
   delegate->ShortWaitForElement(
       selector,
       base::BindOnce(&FocusElementAction::OnWaitForElement,
                      weak_ptr_factory_.GetWeakPtr(), base::Unretained(delegate),
-                     std::move(callback), selector));
+                     std::move(callback), selector, top_padding));
 }
 
 void FocusElementAction::OnWaitForElement(ActionDelegate* delegate,
                                           ProcessActionCallback callback,
                                           const Selector& selector,
+                                          const TopPadding& top_padding,
                                           bool element_found) {
   if (!element_found) {
     UpdateProcessedAction(ELEMENT_RESOLUTION_FAILED);
@@ -52,7 +71,7 @@
   }
 
   delegate->FocusElement(
-      selector,
+      selector, top_padding,
       base::BindOnce(&FocusElementAction::OnFocusElement,
                      weak_ptr_factory_.GetWeakPtr(), base::Unretained(delegate),
                      std::move(callback)));
diff --git a/components/autofill_assistant/browser/actions/focus_element_action.h b/components/autofill_assistant/browser/actions/focus_element_action.h
index ff9ff0c..2f707bb 100644
--- a/components/autofill_assistant/browser/actions/focus_element_action.h
+++ b/components/autofill_assistant/browser/actions/focus_element_action.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_FOCUS_ELEMENT_ACTION_H_
 
 #include "components/autofill_assistant/browser/actions/action.h"
+#include "components/autofill_assistant/browser/top_padding.h"
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
@@ -26,6 +27,7 @@
   void OnWaitForElement(ActionDelegate* delegate,
                         ProcessActionCallback callback,
                         const Selector& selector,
+                        const TopPadding& top_padding,
                         bool element_found);
   void OnFocusElement(ActionDelegate* delegate,
                       ProcessActionCallback callback,
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index a446f28b..365b965 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -14,6 +14,7 @@
 #include "components/autofill_assistant/browser/actions/action_delegate.h"
 #include "components/autofill_assistant/browser/client_settings.h"
 #include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/top_padding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
 namespace autofill_assistant {
@@ -89,8 +90,9 @@
                void(const Selector& selector,
                     const std::string& selected_option,
                     base::OnceCallback<void(const ClientStatus&)> callback));
-  MOCK_METHOD2(FocusElement,
+  MOCK_METHOD3(FocusElement,
                void(const Selector& selector,
+                    const TopPadding& top_padding,
                     base::OnceCallback<void(const ClientStatus&)> callback));
   MOCK_METHOD1(SetTouchableElementArea,
                void(const ElementAreaProto& touchable_element_area));
diff --git a/components/autofill_assistant/browser/mock_web_controller.h b/components/autofill_assistant/browser/mock_web_controller.h
index a725fd8d..e96b8e73 100644
--- a/components/autofill_assistant/browser/mock_web_controller.h
+++ b/components/autofill_assistant/browser/mock_web_controller.h
@@ -10,6 +10,7 @@
 
 #include "base/callback.h"
 #include "components/autofill_assistant/browser/actions/click_action.h"
+#include "components/autofill_assistant/browser/top_padding.h"
 #include "components/autofill_assistant/browser/web_controller.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
@@ -37,11 +38,13 @@
 
   void FocusElement(
       const Selector& selector,
+      const TopPadding& top_padding,
       base::OnceCallback<void(const ClientStatus&)> callback) override {
-    OnFocusElement(selector, callback);
+    OnFocusElement(selector, top_padding, callback);
   }
-  MOCK_METHOD2(OnFocusElement,
+  MOCK_METHOD3(OnFocusElement,
                void(const Selector& selector,
+                    const TopPadding& top_padding,
                     base::OnceCallback<void(const ClientStatus&)>& callback));
 
   void ElementCheck(const Selector& selector,
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 678f3dd..62beb8c2 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -314,9 +314,12 @@
 
 void ScriptExecutor::FocusElement(
     const Selector& selector,
+    const TopPadding& top_padding,
     base::OnceCallback<void(const ClientStatus&)> callback) {
   last_focused_element_selector_ = selector;
-  delegate_->GetWebController()->FocusElement(selector, std::move(callback));
+  last_focused_element_top_padding_ = top_padding;
+  delegate_->GetWebController()->FocusElement(selector, top_padding,
+                                              std::move(callback));
 }
 
 void ScriptExecutor::SetTouchableElementArea(
@@ -886,7 +889,8 @@
 
   if (!main_script_->last_focused_element_selector_.empty()) {
     delegate_->GetWebController()->FocusElement(
-        main_script_->last_focused_element_selector_, base::DoNothing());
+        main_script_->last_focused_element_selector_,
+        main_script_->last_focused_element_top_padding_, base::DoNothing());
   }
 }
 
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index f3bcedb2..05faccc8 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -25,6 +25,7 @@
 #include "components/autofill_assistant/browser/script.h"
 #include "components/autofill_assistant/browser/script_executor_delegate.h"
 #include "components/autofill_assistant/browser/service.pb.h"
+#include "components/autofill_assistant/browser/top_padding.h"
 
 namespace autofill_assistant {
 // Class to execute an assistant script.
@@ -137,6 +138,7 @@
       base::OnceCallback<void(const ClientStatus&)> callback) override;
   void FocusElement(
       const Selector& selector,
+      const TopPadding& top_padding,
       base::OnceCallback<void(const ClientStatus&)> callback) override;
   void SetTouchableElementArea(
       const ElementAreaProto& touchable_element_area) override;
@@ -342,6 +344,7 @@
   bool should_clean_contextual_ui_on_finish_;
   ActionProto::ActionInfoCase previous_action_type_;
   Selector last_focused_element_selector_;
+  TopPadding last_focused_element_top_padding_;
   std::unique_ptr<ElementAreaProto> touchable_element_area_;
   std::map<std::string, ScriptStatusProto>* scripts_state_;
   std::unique_ptr<BatchElementChecker> batch_element_checker_;
diff --git a/components/autofill_assistant/browser/script_executor_unittest.cc b/components/autofill_assistant/browser/script_executor_unittest.cc
index 3aebc40..819580b 100644
--- a/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -67,8 +67,8 @@
 
     ON_CALL(mock_web_controller_, OnElementCheck(_, _))
         .WillByDefault(RunOnceCallback<1>(true));
-    ON_CALL(mock_web_controller_, OnFocusElement(_, _))
-        .WillByDefault(RunOnceCallback<1>(OkClientStatus()));
+    ON_CALL(mock_web_controller_, OnFocusElement(_, _, _))
+        .WillByDefault(RunOnceCallback<2>(OkClientStatus()));
   }
 
  protected:
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index e2cf26c1..1ac036ca1 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -713,6 +713,14 @@
 
 // Contain all arguments to focus on an element.
 message FocusElementProto {
+  message TopPadding {
+    oneof top_padding {
+      // Padding in CSS pixels. Eg. 20.
+      int32 pixels = 1;
+      // Ratio in relation to the window.innerHeight. Eg. 0.25.
+      float ratio = 2;
+    }
+  }
   // Element to focus on.
   optional ElementReferenceProto element = 1;
 
@@ -727,6 +735,9 @@
 
   // Restrict interaction to a series of rectangular areas.
   optional ElementAreaProto touchable_element_area = 6;
+
+  // The padding that will be added between the focused element and the top.
+  optional TopPadding top_padding = 7;
 }
 
 // An area made up of rectangles whole border are made defined by the position
diff --git a/components/autofill_assistant/browser/top_padding.cc b/components/autofill_assistant/browser/top_padding.cc
new file mode 100644
index 0000000..7415810
--- /dev/null
+++ b/components/autofill_assistant/browser/top_padding.cc
@@ -0,0 +1,27 @@
+// Copyright 2018 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/top_padding.h"
+
+namespace autofill_assistant {
+
+TopPadding::TopPadding() {}
+
+TopPadding::TopPadding(float val, Unit u) : value_(val), unit_(u) {}
+
+float TopPadding::pixels() const {
+  if (unit_ == TopPadding::Unit::PIXELS) {
+    return value_;
+  }
+  return 0;
+}
+
+float TopPadding::ratio() const {
+  if (unit_ == TopPadding::Unit::RATIO) {
+    return value_;
+  }
+  return 0;
+}
+
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/top_padding.h b/components/autofill_assistant/browser/top_padding.h
new file mode 100644
index 0000000..472c4c5
--- /dev/null
+++ b/components/autofill_assistant/browser/top_padding.h
@@ -0,0 +1,38 @@
+// 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.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_TOP_PADDING_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_TOP_PADDING_H_
+
+namespace autofill_assistant {
+
+// A simple structure that holds information about the top padding.
+// This structure is used by WebController.FocusElement.
+//
+// Only one type of value can be set (pixels or ratio). If one is
+// set, other returns 0.
+struct TopPadding {
+  enum class Unit {
+    // Css Pixels.
+    PIXELS = 0,
+    // Ratio in relation to window.innerHeight.
+    RATIO = 1
+  };
+
+  TopPadding();
+  TopPadding(float value, Unit unit);
+
+  // Returns 0 if value set in Ratio.
+  float pixels() const;
+  // Returns 0 if value set in CSS Pixels.
+  float ratio() const;
+
+ private:
+  float value_ = 0;
+  Unit unit_ = Unit::PIXELS;
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_TOP_PADDING_H_
diff --git a/components/autofill_assistant/browser/web_controller.cc b/components/autofill_assistant/browser/web_controller.cc
index 9f9963b..fd54c95 100644
--- a/components/autofill_assistant/browser/web_controller.cc
+++ b/components/autofill_assistant/browser/web_controller.cc
@@ -60,15 +60,17 @@
           v.width,
           v.height] })";
 
-const char* const kScrollIntoViewScript =
-    R"(function(node) {
+// Scrolls to the specified node with top padding. The top padding can
+// be specified through pixels or ratio. Pixels take precedence.
+const char* const kScrollIntoViewWithPaddingScript =
+    R"(function(node, topPaddingPixels, topPaddingRatio) {
     node.scrollIntoViewIfNeeded();
     const rect = node.getBoundingClientRect();
-    if (rect.height < window.innerHeight) {
-      window.scrollBy({top: rect.top - window.innerHeight * 0.25});
-    } else {
-      window.scrollBy({top: rect.top});
+    let topPadding = topPaddingPixels;
+    if (!topPadding){
+      topPadding = window.innerHeight * topPaddingRatio;
     }
+    window.scrollBy({top: rect.top - topPadding});
   })";
 
 const char* const kScrollIntoViewIfNeededScript =
@@ -1209,6 +1211,7 @@
 }
 
 void WebController::OnFindElementForFocusElement(
+    const TopPadding& top_padding,
     base::OnceCallback<void(const ClientStatus&)> callback,
     const ClientStatus& status,
     std::unique_ptr<FindElementResult> element_result) {
@@ -1223,11 +1226,12 @@
       settings_->document_ready_check_count, element_object_id,
       base::BindOnce(
           &WebController::OnWaitDocumentToBecomeInteractiveForFocusElement,
-          weak_ptr_factory_.GetWeakPtr(), std::move(callback),
+          weak_ptr_factory_.GetWeakPtr(), top_padding, std::move(callback),
           std::move(element_result)));
 }
 
 void WebController::OnWaitDocumentToBecomeInteractiveForFocusElement(
+    const TopPadding& top_padding,
     base::OnceCallback<void(const ClientStatus&)> callback,
     std::unique_ptr<FindElementResult> target_element,
     bool result) {
@@ -1236,15 +1240,23 @@
     return;
   }
 
-  std::vector<std::unique_ptr<runtime::CallArgument>> argument;
-  argument.emplace_back(runtime::CallArgument::Builder()
-                            .SetObjectId(target_element->object_id)
-                            .Build());
+  std::vector<std::unique_ptr<runtime::CallArgument>> arguments;
+  arguments.emplace_back(runtime::CallArgument::Builder()
+                             .SetObjectId(target_element->object_id)
+                             .Build());
+  arguments.emplace_back(runtime::CallArgument::Builder()
+                             .SetValue(base::Value::ToUniquePtrValue(
+                                 base::Value(top_padding.pixels())))
+                             .Build());
+  arguments.emplace_back(runtime::CallArgument::Builder()
+                             .SetValue(base::Value::ToUniquePtrValue(
+                                 base::Value(top_padding.ratio())))
+                             .Build());
   devtools_client_->GetRuntime()->CallFunctionOn(
       runtime::CallFunctionOnParams::Builder()
           .SetObjectId(target_element->object_id)
-          .SetArguments(std::move(argument))
-          .SetFunctionDeclaration(std::string(kScrollIntoViewScript))
+          .SetArguments(std::move(arguments))
+          .SetFunctionDeclaration(std::string(kScrollIntoViewWithPaddingScript))
           .SetReturnByValue(true)
           .Build(),
       base::BindOnce(&WebController::OnFocusElement,
@@ -1459,14 +1471,15 @@
 
 void WebController::FocusElement(
     const Selector& selector,
+    const TopPadding& top_padding,
     base::OnceCallback<void(const ClientStatus&)> callback) {
   DVLOG(3) << __func__ << " " << selector;
   DCHECK(!selector.empty());
-  FindElement(
-      selector,
-      /* strict_mode= */ false,
-      base::BindOnce(&WebController::OnFindElementForFocusElement,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  FindElement(selector,
+              /* strict_mode= */ false,
+              base::BindOnce(&WebController::OnFindElementForFocusElement,
+                             weak_ptr_factory_.GetWeakPtr(), top_padding,
+                             std::move(callback)));
 }
 
 void WebController::GetFieldValue(
diff --git a/components/autofill_assistant/browser/web_controller.h b/components/autofill_assistant/browser/web_controller.h
index e63a379..8ea31c7 100644
--- a/components/autofill_assistant/browser/web_controller.h
+++ b/components/autofill_assistant/browser/web_controller.h
@@ -23,6 +23,7 @@
 #include "components/autofill_assistant/browser/devtools/devtools_client.h"
 #include "components/autofill_assistant/browser/rectf.h"
 #include "components/autofill_assistant/browser/selector.h"
+#include "components/autofill_assistant/browser/top_padding.h"
 #include "third_party/icu/source/common/unicode/umachine.h"
 #include "url/gurl.h"
 
@@ -105,9 +106,11 @@
       const Selector& selector,
       base::OnceCallback<void(const ClientStatus&)> callback);
 
-  // Focus on element given by |selector|.
+  // Focus on element given by |selector|. |top_padding| specifies the padding
+  // between focused element and the top.
   virtual void FocusElement(
       const Selector& selector,
+      const TopPadding& top_padding,
       base::OnceCallback<void(const ClientStatus&)> callback);
 
   // Get the value of |selector| and return the result through |callback|. The
@@ -321,10 +324,12 @@
       const autofill::FormData& form_data,
       const autofill::FormFieldData& form_field);
   void OnFindElementForFocusElement(
+      const TopPadding& top_padding,
       base::OnceCallback<void(const ClientStatus&)> callback,
       const ClientStatus& status,
       std::unique_ptr<FindElementResult> element_result);
   void OnWaitDocumentToBecomeInteractiveForFocusElement(
+      const TopPadding& top_padding,
       base::OnceCallback<void(const ClientStatus&)> callback,
       std::unique_ptr<FindElementResult> target_element,
       bool result);
diff --git a/components/autofill_assistant/browser/web_controller_browsertest.cc b/components/autofill_assistant/browser/web_controller_browsertest.cc
index e123e5aae..2fc512a 100644
--- a/components/autofill_assistant/browser/web_controller_browsertest.cc
+++ b/components/autofill_assistant/browser/web_controller_browsertest.cc
@@ -4,10 +4,12 @@
 
 #include "base/bind.h"
 #include "base/memory/ref_counted.h"
+#include "base/strings/strcat.h"
 #include "components/autofill_assistant/browser/actions/click_action.h"
 #include "components/autofill_assistant/browser/client_settings.h"
 #include "components/autofill_assistant/browser/service.pb.h"
 #include "components/autofill_assistant/browser/string_conversions_util.h"
+#include "components/autofill_assistant/browser/top_padding.h"
 #include "components/autofill_assistant/browser/web_controller.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_test_utils.h"
@@ -149,10 +151,10 @@
     }
   }
 
-  void FocusElement(const Selector& selector) {
+  void FocusElement(const Selector& selector, const TopPadding top_padding) {
     base::RunLoop run_loop;
     web_controller_->FocusElement(
-        selector,
+        selector, top_padding,
         base::BindOnce(&WebControllerBrowserTest::OnFocusElement,
                        base::Unretained(this), run_loop.QuitClosure()));
     run_loop.Run();
@@ -420,7 +422,8 @@
     Selector selector;
     selector.selectors.emplace_back("#scroll_item_5");
 
-    FocusElement(selector);
+    TopPadding top_padding{0.25, TopPadding::Unit::RATIO};
+    FocusElement(selector, top_padding);
     base::ListValue eval_result = content::EvalJs(shell(), R"(
       let item = document.querySelector("#scroll_item_5");
       let itemRect = item.getBoundingClientRect();
@@ -853,7 +856,8 @@
       iframeRect.y + divRect.y < window.innerHeight;
   )";
   EXPECT_EQ(false, content::EvalJs(shell(), checkVisibleScript));
-  FocusElement(selector);
+  TopPadding top_padding;
+  FocusElement(selector, top_padding);
   EXPECT_EQ(true, content::EvalJs(shell(), checkVisibleScript));
 }
 
@@ -869,6 +873,68 @@
                      /* initial_container_scroll_y=*/200);
 }
 
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
+                       FocusElement_WithPaddingInPixels) {
+  Selector selector;
+  selector.selectors.emplace_back("#scroll-me");
+
+  const std::string checkScrollDifferentThanTargetScript = R"(
+      window.scrollTo(0, 0);
+      let scrollTarget = document.querySelector("#scroll-me");
+      let scrollTargetRect = scrollTarget.getBoundingClientRect();
+      scrollTargetRect.y > 360;
+  )";
+
+  EXPECT_EQ(true,
+            content::EvalJs(shell(), checkScrollDifferentThanTargetScript));
+
+  // Scroll 360px from the top.
+  TopPadding top_padding{/* value= */ 360, TopPadding::Unit::PIXELS};
+  FocusElement(selector, top_padding);
+
+  double eval_result = content::EvalJs(shell(), R"(
+      let scrollTarget = document.querySelector("#scroll-me");
+      let scrollTargetRect = scrollTarget.getBoundingClientRect();
+      scrollTargetRect.top;
+  )")
+                           .ExtractDouble();
+
+  EXPECT_NEAR(360, eval_result, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
+                       FocusElement_WithPaddingInRatio) {
+  Selector selector;
+  selector.selectors.emplace_back("#scroll-me");
+
+  const std::string checkScrollDifferentThanTargetScript = R"(
+      window.scrollTo(0, 0);
+      let scrollTarget = document.querySelector("#scroll-me");
+      let scrollTargetRect = scrollTarget.getBoundingClientRect();
+      let targetScrollY = window.innerHeight * 0.7;
+      scrollTargetRect.y > targetScrollY;
+  )";
+
+  EXPECT_EQ(true,
+            content::EvalJs(shell(), checkScrollDifferentThanTargetScript));
+
+  // Scroll 70% from the top.
+  TopPadding top_padding{/* value= */ 0.7, TopPadding::Unit::RATIO};
+  FocusElement(selector, top_padding);
+
+  base::ListValue eval_result = content::EvalJs(shell(), R"(
+      let scrollTarget = document.querySelector("#scroll-me");
+      let scrollTargetRect = scrollTarget.getBoundingClientRect();
+      [scrollTargetRect.top, window.innerHeight]
+  )")
+                                    .ExtractList();
+
+  double top = eval_result.GetList()[0].GetDouble();
+  double window_inner_height = eval_result.GetList()[1].GetDouble();
+
+  EXPECT_NEAR(top, window_inner_height * 0.7, 1);
+}
+
 IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, SelectOption) {
   Selector selector;
   selector.selectors.emplace_back("#select");
diff --git a/components/test/data/autofill_assistant/autofill_assistant_target_website.html b/components/test/data/autofill_assistant/autofill_assistant_target_website.html
index ee4ca7d..b831945 100644
--- a/components/test/data/autofill_assistant/autofill_assistant_target_website.html
+++ b/components/test/data/autofill_assistant/autofill_assistant_target_website.html
@@ -231,6 +231,8 @@
 
     <div id="hidden" style="display: none;">This text is hidden</div>
 
+    <div id="scroll-me" style="background-color: green; height: 40px; width: 100%;"></div>
+
     <iframe id="iframe" name="test_iframe" width="100%" height="500" src=
         "autofill_assistant_target_website_iframe_one.html"></iframe>