Commit 3e6b699a authored by Mathias Carlen's avatar Mathias Carlen Committed by Commit Bot

[Autofill Assistant] Implement the highlight element action.

This change adds a highlight element action which updates the css shadow of a
given element.

R=gogerald@chromium.org

Bug: 806868
Change-Id: Ife28259e4020721fd87487770b154c452425a48c
Reviewed-on: https://chromium-review.googlesource.com/c/1262555
Commit-Queue: Mathias Carlen <mcarlen@chromium.org>
Reviewed-by: default avatarGanggui Tang <gogerald@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597032}
parent f0077b70
......@@ -22,6 +22,8 @@ jumbo_static_library("browser") {
"actions/click_action.h",
"actions/focus_element_action.cc",
"actions/focus_element_action.h",
"actions/highlight_element_action.cc",
"actions/highlight_element_action.h",
"actions/navigate_action.cc",
"actions/navigate_action.h",
"actions/reset_action.cc",
......
......@@ -75,6 +75,10 @@ class ActionDelegate {
virtual void FocusElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) = 0;
// Highlight the element given by |selectors|.
virtual void HighlightElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) = 0;
// Get the value of |selectors| and return the result through |callback|. The
// returned value will be the empty string in case of error or empty value.
virtual void GetFieldValue(
......
// 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/actions/highlight_element_action.h"
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "components/autofill_assistant/browser/actions/action_delegate.h"
namespace autofill_assistant {
HighlightElementAction::HighlightElementAction(const ActionProto& proto)
: Action(proto), weak_ptr_factory_(this) {
DCHECK(proto_.has_highlight_element());
}
HighlightElementAction::~HighlightElementAction() {}
void HighlightElementAction::ProcessAction(ActionDelegate* delegate,
ProcessActionCallback callback) {
processed_action_proto_ = std::make_unique<ProcessedActionProto>();
const HighlightElementProto& highlight_element = proto_.highlight_element();
std::vector<std::string> selectors;
for (const auto& selector : highlight_element.element().selectors()) {
selectors.emplace_back(selector);
}
DCHECK(!selectors.empty());
delegate->HighlightElement(
selectors,
base::BindOnce(&HighlightElementAction::OnHighlightElement,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void HighlightElementAction::OnHighlightElement(ProcessActionCallback callback,
bool status) {
// TODO(crbug.com/806868): Distinguish element not found from other error and
// report them as ELEMENT_RESOLUTION_FAILED.
UpdateProcessedAction(status ? ACTION_APPLIED : OTHER_ACTION_STATUS);
std::move(callback).Run(std::move(processed_action_proto_));
}
} // namespace autofill_assistant
// 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.
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_HIGHLIGHT_ELEMENT_ACTION_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_HIGHLIGHT_ELEMENT_ACTION_H_
#include "components/autofill_assistant/browser/actions/action.h"
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
namespace autofill_assistant {
// An action to highlight an element on Web.
// TODO(crbug.com/806868): This action should be more configurable instead of
// using hardcoded css styling since it depends on the content of a page.
class HighlightElementAction : public Action {
public:
explicit HighlightElementAction(const ActionProto& proto);
~HighlightElementAction() override;
// Overrides Action:
void ProcessAction(ActionDelegate* delegate,
ProcessActionCallback callback) override;
private:
void OnHighlightElement(ProcessActionCallback callback, bool status);
base::WeakPtrFactory<HighlightElementAction> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(HighlightElementAction);
};
} // namespace autofill_assistant
#endif // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_ACTIONS_HIGHLIGHT_ELEMENT_ACTION_H_
......@@ -73,6 +73,9 @@ class MockActionDelegate : public ActionDelegate {
MOCK_METHOD2(FocusElement,
void(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback));
MOCK_METHOD2(HighlightElement,
void(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback));
void GetFieldValue(
const std::vector<std::string>& selectors,
......
......@@ -10,6 +10,7 @@
#include "components/autofill_assistant/browser/actions/autofill_action.h"
#include "components/autofill_assistant/browser/actions/click_action.h"
#include "components/autofill_assistant/browser/actions/focus_element_action.h"
#include "components/autofill_assistant/browser/actions/highlight_element_action.h"
#include "components/autofill_assistant/browser/actions/navigate_action.h"
#include "components/autofill_assistant/browser/actions/reset_action.h"
#include "components/autofill_assistant/browser/actions/select_option_action.h"
......@@ -190,6 +191,14 @@ bool ProtocolUtils::ParseActions(
actions->emplace_back(std::make_unique<ResetAction>(action));
break;
}
case ActionProto::ActionInfoCase::kHighlightElement: {
actions->emplace_back(std::make_unique<HighlightElementAction>(action));
break;
}
case ActionProto::ActionInfoCase::kUploadDom: {
actions->emplace_back(std::make_unique<UploadDomAction>(action));
break;
}
default:
case ActionProto::ActionInfoCase::ACTION_INFO_NOT_SET: {
DLOG(ERROR) << "Unknown or unsupported action with action_case="
......
......@@ -86,6 +86,12 @@ void ScriptExecutor::SelectOption(const std::vector<std::string>& selectors,
std::move(callback));
}
void ScriptExecutor::HighlightElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) {
delegate_->GetWebController()->HighlightElement(selectors,
std::move(callback));
}
void ScriptExecutor::FocusElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) {
delegate_->GetWebController()->FocusElement(selectors, std::move(callback));
......
......@@ -68,6 +68,8 @@ class ScriptExecutor : public ActionDelegate {
void SelectOption(const std::vector<std::string>& selectors,
const std::string& selected_option,
base::OnceCallback<void(bool)> callback) override;
void HighlightElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) override;
void FocusElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) override;
void GetFieldValue(
......
......@@ -194,6 +194,7 @@ message ActionProto {
UseCreditCardProto use_card = 28;
UseAddressProto use_address = 29;
UploadDomProto upload_dom = 18;
HighlightElementProto highlight_element = 31;
ResetProto reset = 34;
StopProto stop = 35;
}
......@@ -333,6 +334,12 @@ message UploadDomProto {
optional ElementReferenceProto tree_root = 1;
}
// Contain all arguments to perform a highlight element action.
message HighlightElementProto {
// The element to highlight.
optional ElementReferenceProto element = 1;
}
// Load the given URL in the current tab.
message NavigateProto {
optional string url = 1;
......
......@@ -56,6 +56,14 @@ const char* const kSelectOptionScript =
return true;
})";
// Javascript to highlight an element.
const char* const kHighlightElementScript =
R"(function() {
this.style.boxShadow = '0px 0px 0px 3px white, ' +
'0px 0px 0px 6px rgb(66, 133, 244)';
return true;
})";
// Javascript code to retrieve the 'value' attribute of a node.
const char* const kGetValueAttributeScript =
"function () { return this.value; }";
......@@ -614,6 +622,53 @@ void WebController::OnSelectOption(
OnResult(result->GetResult()->GetValue()->GetBool(), std::move(callback));
}
void WebController::HighlightElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) {
FindElement(
selectors,
base::BindOnce(&WebController::OnFindElementForHighlightElement,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void WebController::OnFindElementForHighlightElement(
base::OnceCallback<void(bool)> callback,
std::unique_ptr<FindElementResult> element_result) {
const std::string object_id = element_result->object_id;
if (object_id.empty()) {
DLOG(ERROR) << "Failed to find the element to highlight.";
OnResult(false, std::move(callback));
return;
}
std::vector<std::unique_ptr<runtime::CallArgument>> argument;
argument.emplace_back(
runtime::CallArgument::Builder().SetObjectId(object_id).Build());
devtools_client_->GetRuntime()->Enable();
devtools_client_->GetRuntime()->CallFunctionOn(
runtime::CallFunctionOnParams::Builder()
.SetObjectId(object_id)
.SetArguments(std::move(argument))
.SetFunctionDeclaration(std::string(kHighlightElementScript))
.SetReturnByValue(true)
.Build(),
base::BindOnce(&WebController::OnHighlightElement,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void WebController::OnHighlightElement(
base::OnceCallback<void(bool)> callback,
std::unique_ptr<runtime::CallFunctionOnResult> result) {
devtools_client_->GetRuntime()->Disable();
if (!result || result->HasExceptionDetails()) {
DLOG(ERROR) << "Failed to highlight element.";
OnResult(false, std::move(callback));
return;
}
// Read the result returned from Javascript code.
DCHECK(result->GetResult()->GetValue()->is_bool());
OnResult(result->GetResult()->GetValue()->GetBool(), std::move(callback));
}
void WebController::FocusElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) {
DCHECK(!selectors.empty());
......
......@@ -90,6 +90,10 @@ class WebController {
const std::string& selected_option,
base::OnceCallback<void(bool)> callback);
// Highlight an element given by |selectors|.
virtual void HighlightElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback);
// Focus on element given by |selectors|.
virtual void FocusElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback);
......@@ -228,6 +232,12 @@ class WebController {
std::unique_ptr<FindElementResult> element_result);
void OnSelectOption(base::OnceCallback<void(bool)> callback,
std::unique_ptr<runtime::CallFunctionOnResult> result);
void OnFindElementForHighlightElement(
base::OnceCallback<void(bool)> callback,
std::unique_ptr<FindElementResult> element_result);
void OnHighlightElement(
base::OnceCallback<void(bool)> callback,
std::unique_ptr<runtime::CallFunctionOnResult> result);
void OnFindElementForGetFieldValue(
base::OnceCallback<void(const std::string&)> callback,
std::unique_ptr<FindElementResult> element_result);
......
......@@ -120,6 +120,24 @@ class WebControllerBrowserTest : public content::ContentBrowserTest {
std::move(done_callback).Run();
}
bool HighlightElement(const std::vector<std::string>& selectors) {
base::RunLoop run_loop;
bool result;
web_controller_->HighlightElement(
selectors, base::BindOnce(&WebControllerBrowserTest::OnHighlightElement,
base::Unretained(this),
run_loop.QuitClosure(), &result));
run_loop.Run();
return result;
}
void OnHighlightElement(base::Closure done_callback,
bool* result_output,
bool result) {
*result_output = result;
std::move(done_callback).Run();
}
bool GetOuterHtml(const std::vector<std::string>& selectors,
std::string* html_output) {
base::RunLoop run_loop;
......@@ -416,4 +434,19 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, NavigateToUrl) {
EXPECT_EQ(url::kAboutBlankURL, web_controller_->GetUrl().spec());
}
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, HighlightElement) {
std::vector<std::string> selectors;
selectors.emplace_back("#select");
const std::string javascript = R"(
let select = document.querySelector("#select");
select.style.boxShadow;
)";
EXPECT_EQ("", content::EvalJs(shell(), javascript));
ASSERT_TRUE(HighlightElement(selectors));
// We only make sure that the element has a non-empty boxShadow style without
// requiring an exact string match.
EXPECT_NE("", content::EvalJs(shell(), javascript));
}
} // namespace
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment