Commit a54cfbac authored by Stephane Zermatten's avatar Stephane Zermatten Committed by Commit Bot

[Autofill Assistant] Support script precondition on URL domain and path.

This change adds support for preconditions on URL,
ScriptPreconditionProto.domain and path_pattern.

Bug: 806868
Change-Id: I19d93b8083e67e7455a10e29535b082c2b642b2f
Reviewed-on: https://chromium-review.googlesource.com/1203374
Commit-Queue: Stephane Zermatten <szermatt@chromium.org>
Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Reviewed-by: default avatarGanggui Tang <gogerald@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590028}
parent 143f1e0f
...@@ -57,6 +57,7 @@ jumbo_static_library("browser") { ...@@ -57,6 +57,7 @@ jumbo_static_library("browser") {
"//content/public/browser", "//content/public/browser",
"//google_apis", "//google_apis",
"//net", "//net",
"//third_party/re2",
] ]
} }
...@@ -72,6 +73,7 @@ source_set("unit_tests") { ...@@ -72,6 +73,7 @@ source_set("unit_tests") {
"mock_web_controller.cc", "mock_web_controller.cc",
"mock_web_controller.h", "mock_web_controller.h",
"script_executor_unittest.cc", "script_executor_unittest.cc",
"script_precondition_unittest.cc",
"script_tracker_unittest.cc", "script_tracker_unittest.cc",
] ]
...@@ -83,5 +85,6 @@ source_set("unit_tests") { ...@@ -83,5 +85,6 @@ source_set("unit_tests") {
"//content/test:test_support", "//content/test:test_support",
"//testing/gmock", "//testing/gmock",
"//testing/gtest", "//testing/gtest",
"//third_party/re2",
] ]
} }
...@@ -6,4 +6,5 @@ include_rules = [ ...@@ -6,4 +6,5 @@ include_rules = [
"+google_apis", "+google_apis",
"+net", "+net",
"+services/network/public/cpp", "+services/network/public/cpp",
"+third_party/re2",
] ]
...@@ -25,6 +25,7 @@ using ::testing::Eq; ...@@ -25,6 +25,7 @@ using ::testing::Eq;
using ::testing::NiceMock; using ::testing::NiceMock;
using ::testing::SizeIs; using ::testing::SizeIs;
using ::testing::StrEq; using ::testing::StrEq;
using ::testing::ReturnRef;
namespace { namespace {
...@@ -74,6 +75,9 @@ class ControllerTest : public content::RenderViewHostTestHarness { ...@@ -74,6 +75,9 @@ class ControllerTest : public content::RenderViewHostTestHarness {
ON_CALL(*mock_web_controller_, OnElementExists(ElementsAre("exists"), _)) ON_CALL(*mock_web_controller_, OnElementExists(ElementsAre("exists"), _))
.WillByDefault(RunOnceCallback<1>(true)); .WillByDefault(RunOnceCallback<1>(true));
// Make WebController::GetUrl accessible.
ON_CALL(*mock_web_controller_, GetUrl()).WillByDefault(ReturnRef(url_));
tester_ = content::WebContentsTester::For(web_contents()); tester_ = content::WebContentsTester::For(web_contents());
} }
...@@ -113,6 +117,7 @@ class ControllerTest : public content::RenderViewHostTestHarness { ...@@ -113,6 +117,7 @@ class ControllerTest : public content::RenderViewHostTestHarness {
UiDelegate* GetUiDelegate() { return controller_; } UiDelegate* GetUiDelegate() { return controller_; }
GURL url_;
MockService* mock_service_; MockService* mock_service_;
MockWebController* mock_web_controller_; MockWebController* mock_web_controller_;
MockUiController* mock_ui_controller_; MockUiController* mock_ui_controller_;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
namespace autofill_assistant { namespace autofill_assistant {
MockWebController::MockWebController() : WebController(nullptr) {} MockWebController::MockWebController() : WebController(nullptr, nullptr) {}
MockWebController::~MockWebController() {} MockWebController::~MockWebController() {}
} // namespace autofill_assistant } // namespace autofill_assistant
...@@ -19,6 +19,8 @@ class MockWebController : public WebController { ...@@ -19,6 +19,8 @@ class MockWebController : public WebController {
MockWebController(); MockWebController();
~MockWebController() override; ~MockWebController() override;
MOCK_METHOD0(GetUrl, const GURL&());
void ClickElement(const std::vector<std::string>& selectors, void ClickElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) override { base::OnceCallback<void(bool)> callback) override {
// Transforming callback into a references allows using RunOnceCallback on // Transforming callback into a references allows using RunOnceCallback on
......
...@@ -49,27 +49,19 @@ bool ProtocolUtils::ParseScripts( ...@@ -49,27 +49,19 @@ bool ProtocolUtils::ParseScripts(
auto script = std::make_unique<Script>(); auto script = std::make_unique<Script>();
script->handle.path = script_proto.path(); script->handle.path = script_proto.path();
if (script_proto.has_presentation()) { const auto& presentation = script_proto.presentation();
const auto& presentation = script_proto.presentation(); script->handle.name = presentation.name();
if (presentation.has_name())
script->handle.name = presentation.name(); if (presentation.has_precondition()) {
script->precondition =
if (presentation.has_precondition()) { ScriptPrecondition::FromProto(presentation.precondition());
std::vector<std::vector<std::string>> elements_exist; }
for (const auto& element :
presentation.precondition().elements_exist()) { if (script->handle.name.empty() || script->handle.path.empty() ||
std::vector<std::string> selectors; !script->precondition) {
for (const auto& selector : element.selectors()) { LOG(ERROR) << "Ignored invalid or incomplete script '"
selectors.emplace_back(selector); << script->handle.path << "'";
} continue;
elements_exist.emplace_back(selectors);
}
if (!elements_exist.empty()) {
script->precondition =
std::make_unique<ScriptPrecondition>(elements_exist);
}
}
} }
scripts->emplace_back(std::move(script)); scripts->emplace_back(std::move(script));
} }
......
...@@ -28,8 +28,12 @@ class ProtocolUtils { ...@@ -28,8 +28,12 @@ class ProtocolUtils {
using Scripts = std::map<Script*, std::unique_ptr<Script>>; using Scripts = std::map<Script*, std::unique_ptr<Script>>;
// Parse assistant scripts from the given |response|, which should not be an // Parse assistant scripts from the given |response|, which should not be an
// empty string. // empty string.
// Parsed assistant scripts are returned through |scripts|, which //
// should not be nullptr. Return false if parse failed, otherwise return true. // Parsed assistant scripts are returned through |scripts|, which should not
// be nullptr. Returned scripts are guaranteed to be fully initialized, and
// have a name, path and precondition.
//
// Return false if parse failed, otherwise return true.
static bool ParseScripts(const std::string& response, static bool ParseScripts(const std::string& response,
std::vector<std::unique_ptr<Script>>* scripts); std::vector<std::unique_ptr<Script>>* scripts);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_H_ #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_H_ #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_H_
#include <memory>
#include <string> #include <string>
#include "components/autofill_assistant/browser/script_precondition.h" #include "components/autofill_assistant/browser/script_precondition.h"
......
...@@ -4,19 +4,64 @@ ...@@ -4,19 +4,64 @@
#include "components/autofill_assistant/browser/script_precondition.h" #include "components/autofill_assistant/browser/script_precondition.h"
#include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "third_party/re2/src/re2/re2.h"
#include "url/gurl.h"
namespace autofill_assistant { namespace autofill_assistant {
ScriptPrecondition::ScriptPrecondition( // Static
const std::vector<std::vector<std::string>>& elements_exist) std::unique_ptr<ScriptPrecondition> ScriptPrecondition::FromProto(
: elements_exist_(elements_exist), weak_ptr_factory_(this) {} const ScriptPreconditionProto& proto) {
std::vector<std::vector<std::string>> elements_exist;
for (const auto& element : proto.elements_exist()) {
std::vector<std::string> selectors;
for (const auto& selector : element.selectors()) {
selectors.emplace_back(selector);
}
elements_exist.emplace_back(selectors);
}
std::set<std::string> domain_match;
for (const auto& domain : proto.domain()) {
domain_match.emplace(domain);
}
std::vector<std::unique_ptr<re2::RE2>> path_pattern;
for (const auto& pattern : proto.path_pattern()) {
auto re = std::make_unique<re2::RE2>(pattern);
if (re->error_code() != re2::RE2::NoError) {
LOG(ERROR) << "Invalid regexp in script precondition '" << pattern
<< "'. Will never match.";
return nullptr;
}
path_pattern.emplace_back(std::move(re));
}
// TODO(crbug.com/806868): Detect unknown or unsupported conditions and reject
// them.
return std::make_unique<ScriptPrecondition>(elements_exist, domain_match,
std::move(path_pattern));
}
ScriptPrecondition::~ScriptPrecondition() {} ScriptPrecondition::~ScriptPrecondition() {}
void ScriptPrecondition::Check(WebController* web_controller, void ScriptPrecondition::Check(WebController* web_controller,
base::OnceCallback<void(bool)> callback) { base::OnceCallback<void(bool)> callback) {
DCHECK(!elements_exist_.empty()); const GURL& url = web_controller->GetUrl();
if (!MatchDomain(url) || !MatchPath(url)) {
std::move(callback).Run(false);
return;
}
if (elements_exist_.empty()) {
std::move(callback).Run(true);
return;
}
check_preconditions_callback_ = std::move(callback); check_preconditions_callback_ = std::move(callback);
pending_elements_exist_check_ = 0; pending_elements_exist_check_ = 0;
...@@ -28,6 +73,15 @@ void ScriptPrecondition::Check(WebController* web_controller, ...@@ -28,6 +73,15 @@ void ScriptPrecondition::Check(WebController* web_controller,
} }
} }
ScriptPrecondition::ScriptPrecondition(
const std::vector<std::vector<std::string>>& elements_exist,
const std::set<std::string>& domain_match,
std::vector<std::unique_ptr<re2::RE2>> path_pattern)
: elements_exist_(elements_exist),
domain_match_(domain_match),
path_pattern_(std::move(path_pattern)),
weak_ptr_factory_(this) {}
void ScriptPrecondition::OnCheckElementExists(bool result) { void ScriptPrecondition::OnCheckElementExists(bool result) {
pending_elements_exist_check_--; pending_elements_exist_check_--;
// Return false early if there is a check failed. // Return false early if there is a check failed.
...@@ -42,4 +96,25 @@ void ScriptPrecondition::OnCheckElementExists(bool result) { ...@@ -42,4 +96,25 @@ void ScriptPrecondition::OnCheckElementExists(bool result) {
} }
} }
bool ScriptPrecondition::MatchDomain(const GURL& url) const {
if (domain_match_.empty())
return true;
return domain_match_.find(url.host()) != domain_match_.end();
}
bool ScriptPrecondition::MatchPath(const GURL& url) const {
if (path_pattern_.empty()) {
return true;
}
const std::string path = url.path();
for (auto& regexp : path_pattern_) {
if (regexp->Match(path, 0, path.size(), re2::RE2::UNANCHORED, NULL, 0)) {
return true;
}
}
return false;
}
} // namespace autofill_assistant. } // namespace autofill_assistant.
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_PRECONDITION_H_ #ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_PRECONDITION_H_
#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_PRECONDITION_H_ #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_PRECONDITION_H_
#include <memory>
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
...@@ -13,12 +15,25 @@ ...@@ -13,12 +15,25 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "components/autofill_assistant/browser/web_controller.h" #include "components/autofill_assistant/browser/web_controller.h"
namespace re2 {
class RE2;
} // namespace re2
namespace autofill_assistant { namespace autofill_assistant {
class ScriptPreconditionProto;
// Class represents a set of preconditions for a script to be executed. // Class represents a set of preconditions for a script to be executed.
class ScriptPrecondition { class ScriptPrecondition {
public: public:
explicit ScriptPrecondition( // Builds a precondition from its proto representation. Returns nullptr if the
const std::vector<std::vector<std::string>>& elements_exist); // preconditions are invalid.
static std::unique_ptr<ScriptPrecondition> FromProto(
const ScriptPreconditionProto& proto);
ScriptPrecondition(
const std::vector<std::vector<std::string>>& elements_exist,
const std::set<std::string>& domain_match,
std::vector<std::unique_ptr<re2::RE2>> path_pattern);
~ScriptPrecondition(); ~ScriptPrecondition();
// Check whether the conditions satisfied and return the result through // Check whether the conditions satisfied and return the result through
...@@ -28,11 +43,19 @@ class ScriptPrecondition { ...@@ -28,11 +43,19 @@ class ScriptPrecondition {
private: private:
void OnCheckElementExists(bool result); void OnCheckElementExists(bool result);
bool MatchDomain(const GURL& url) const;
bool MatchPath(const GURL& url) const;
std::vector<std::vector<std::string>> elements_exist_; std::vector<std::vector<std::string>> elements_exist_;
base::OnceCallback<void(bool)> check_preconditions_callback_; base::OnceCallback<void(bool)> check_preconditions_callback_;
size_t pending_elements_exist_check_; size_t pending_elements_exist_check_;
// Domain (exact match) excluding the last '/' character.
std::set<std::string> domain_match_;
// Pattern of the path parts of the URL.
std::vector<std::unique_ptr<re2::RE2>> path_pattern_;
base::WeakPtrFactory<ScriptPrecondition> weak_ptr_factory_; base::WeakPtrFactory<ScriptPrecondition> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ScriptPrecondition); DISALLOW_COPY_AND_ASSIGN(ScriptPrecondition);
......
// 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/script_precondition.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/macros.h"
#include "components/autofill_assistant/browser/mock_run_once_callback.h"
#include "components/autofill_assistant/browser/mock_web_controller.h"
#include "components/autofill_assistant/browser/service.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/re2/src/re2/re2.h"
namespace autofill_assistant {
namespace {
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Invoke;
// A callback that expects to be called immediately.
//
// This relies on ScriptPrecondition and WebController calling the callback
// immediately, which is not true in general, but is in this test.
class DirectCallback {
public:
DirectCallback() : was_run_(false), result_(false) {}
// Returns a base::OnceCallback. The current instance must exist until
// GetResultOrDie is called.
base::OnceCallback<void(bool)> Get() {
return base::BindOnce(&DirectCallback::Run, base::Unretained(this));
}
bool GetResultOrDie() {
CHECK(was_run_);
return result_;
}
private:
void Run(bool result) {
was_run_ = true;
result_ = result;
}
bool was_run_;
bool result_;
};
class ScriptPreconditionTest : public testing::Test {
public:
void SetUp() override {
ON_CALL(mock_web_controller_, OnElementExists(ElementsAre("exists"), _))
.WillByDefault(RunOnceCallback<1>(true));
ON_CALL(mock_web_controller_,
OnElementExists(ElementsAre("does_not_exist"), _))
.WillByDefault(RunOnceCallback<1>(false));
SetUrl("http://www.example.com/path");
ON_CALL(mock_web_controller_, GetUrl())
.WillByDefault(Invoke(this, &ScriptPreconditionTest::GetUrl));
}
protected:
// Implements WebController::GetUrl
const GURL& GetUrl() { return url_; }
void SetUrl(const std::string& url) { url_ = GURL(url); }
// Runs the preconditions and returns the result.
bool Check(const ScriptPreconditionProto& proto) {
auto precondition = ScriptPrecondition::FromProto(proto);
if (!precondition)
return false;
DirectCallback callback;
precondition->Check(&mock_web_controller_, callback.Get());
return callback.GetResultOrDie();
}
GURL url_;
MockWebController mock_web_controller_;
};
TEST_F(ScriptPreconditionTest, NoConditions) {
EXPECT_TRUE(Check(ScriptPreconditionProto::default_instance()));
}
TEST_F(ScriptPreconditionTest, DomainMatch) {
ScriptPreconditionProto proto;
proto.add_domain("match.example.com");
proto.add_domain("alsomatch.example.com");
SetUrl("http://match.example.com/path");
EXPECT_TRUE(Check(proto));
SetUrl("http://nomatch.example.com/path");
EXPECT_FALSE(Check(proto));
SetUrl("http://alsomatch.example.com/path");
EXPECT_TRUE(Check(proto));
}
TEST_F(ScriptPreconditionTest, PathFullMatch) {
ScriptPreconditionProto proto;
proto.add_path_pattern("/match.*");
proto.add_path_pattern("/alsomatch");
SetUrl("http://www.example.com/match1");
EXPECT_TRUE(Check(proto));
SetUrl("http://www.example.com/match123");
EXPECT_TRUE(Check(proto));
SetUrl("http://www.example.com/doesnotmatch");
EXPECT_FALSE(Check(proto));
SetUrl("http://www.example.com/alsomatch");
EXPECT_TRUE(Check(proto));
}
TEST_F(ScriptPreconditionTest, PathPartialMatch) {
ScriptPreconditionProto proto;
proto.add_path_pattern("/match");
SetUrl("http://www.example.com/prefix/match/suffix");
EXPECT_TRUE(Check(proto));
}
TEST_F(ScriptPreconditionTest, BadPathPattern) {
ScriptPreconditionProto proto;
proto.add_path_pattern("invalid[");
EXPECT_EQ(nullptr, ScriptPrecondition::FromProto(proto));
}
TEST_F(ScriptPreconditionTest, MultipleConditions) {
ScriptPreconditionProto proto;
proto.add_domain("match.example.com");
proto.add_path_pattern("/path");
proto.add_elements_exist()->add_selectors("exists");
// Domain and path don't match.
EXPECT_FALSE(Check(proto));
// Domain, path and selector match.
SetUrl("http://match.example.com/path");
EXPECT_TRUE(Check(proto));
// Selector doesn't match.
proto.mutable_elements_exist(0)->set_selectors(0, "does_not_exist");
EXPECT_FALSE(Check(proto));
}
} // namespace
} // namespace autofill_assistant
...@@ -20,9 +20,10 @@ ...@@ -20,9 +20,10 @@
namespace autofill_assistant { namespace autofill_assistant {
using ::testing::_; using ::testing::_;
using ::testing::ElementsAre; using ::testing::ElementsAre;
using ::testing::SizeIs;
using ::testing::IsEmpty; using ::testing::IsEmpty;
using ::testing::NiceMock; using ::testing::NiceMock;
using ::testing::ReturnRef;
using ::testing::SizeIs;
class ScriptTrackerTest : public testing::Test, class ScriptTrackerTest : public testing::Test,
public ScriptTracker::Listener, public ScriptTracker::Listener,
...@@ -34,6 +35,7 @@ class ScriptTrackerTest : public testing::Test, ...@@ -34,6 +35,7 @@ class ScriptTrackerTest : public testing::Test,
ON_CALL(mock_web_controller_, ON_CALL(mock_web_controller_,
OnElementExists(ElementsAre("does_not_exist"), _)) OnElementExists(ElementsAre("does_not_exist"), _))
.WillByDefault(RunOnceCallback<1>(false)); .WillByDefault(RunOnceCallback<1>(false));
ON_CALL(mock_web_controller_, GetUrl()).WillByDefault(ReturnRef(url_));
// Scripts run, but have no actions. // Scripts run, but have no actions.
ON_CALL(mock_service_, OnGetActions(_, _)) ON_CALL(mock_service_, OnGetActions(_, _))
...@@ -84,6 +86,7 @@ class ScriptTrackerTest : public testing::Test, ...@@ -84,6 +86,7 @@ class ScriptTrackerTest : public testing::Test,
return runnable_scripts_; return runnable_scripts_;
} }
GURL url_;
NiceMock<MockService> mock_service_; NiceMock<MockService> mock_service_;
NiceMock<MockWebController> mock_web_controller_; NiceMock<MockWebController> mock_web_controller_;
NiceMock<MockUiController> mock_ui_controller_; NiceMock<MockUiController> mock_ui_controller_;
......
...@@ -42,7 +42,11 @@ message SupportedScriptProto { ...@@ -42,7 +42,11 @@ message SupportedScriptProto {
message ScriptPreconditionProto { message ScriptPreconditionProto {
// Combined with AND: the elements referenced here must be present. // Combined with AND: the elements referenced here must be present.
repeated ElementReferenceProto elements_exist = 1; repeated ElementReferenceProto elements_exist = 3;
// Pattern of the path parts of the URL.
repeated string path_pattern = 5;
// Domain (exact match) excluding the last '/' character.
repeated string domain = 6;
} }
enum PolicyType { enum PolicyType {
......
...@@ -27,15 +27,24 @@ const char* const kScrollIntoViewScript = ...@@ -27,15 +27,24 @@ const char* const kScrollIntoViewScript =
// static // static
std::unique_ptr<WebController> WebController::CreateForWebContents( std::unique_ptr<WebController> WebController::CreateForWebContents(
content::WebContents* web_contents) { content::WebContents* web_contents) {
return std::make_unique<WebController>(std::make_unique<DevtoolsClient>( return std::make_unique<WebController>(
content::DevToolsAgentHost::GetOrCreateFor(web_contents))); web_contents,
std::make_unique<DevtoolsClient>(
content::DevToolsAgentHost::GetOrCreateFor(web_contents)));
} }
WebController::WebController(std::unique_ptr<DevtoolsClient> devtools_client) WebController::WebController(content::WebContents* web_contents,
: devtools_client_(std::move(devtools_client)), weak_ptr_factory_(this) {} std::unique_ptr<DevtoolsClient> devtools_client)
: web_contents_(web_contents),
devtools_client_(std::move(devtools_client)),
weak_ptr_factory_(this) {}
WebController::~WebController() {} WebController::~WebController() {}
const GURL& WebController::GetUrl() {
return web_contents_->GetLastCommittedURL();
}
void WebController::ClickElement(const std::vector<std::string>& selectors, void WebController::ClickElement(const std::vector<std::string>& selectors,
base::OnceCallback<void(bool)> callback) { base::OnceCallback<void(bool)> callback) {
DCHECK(!selectors.empty()); DCHECK(!selectors.empty());
......
...@@ -29,9 +29,14 @@ class WebController { ...@@ -29,9 +29,14 @@ class WebController {
static std::unique_ptr<WebController> CreateForWebContents( static std::unique_ptr<WebController> CreateForWebContents(
content::WebContents* web_contents); content::WebContents* web_contents);
explicit WebController(std::unique_ptr<DevtoolsClient> devtools_client); // |web_contents| must outlive this web controller.
WebController(content::WebContents* web_contents,
std::unique_ptr<DevtoolsClient> devtools_client);
virtual ~WebController(); virtual ~WebController();
// Returns the last committed URL of the associated |web_contents_|.
virtual const GURL& GetUrl();
// Perform a mouse left button click on the element given by |selectors| and // Perform a mouse left button click on the element given by |selectors| and
// return the result through callback. // return the result through callback.
// CSS selectors in |selectors| are ordered from top frame to the frame // CSS selectors in |selectors| are ordered from top frame to the frame
...@@ -100,6 +105,9 @@ class WebController { ...@@ -100,6 +105,9 @@ class WebController {
std::unique_ptr<dom::PushNodesByBackendIdsToFrontendResult> result); std::unique_ptr<dom::PushNodesByBackendIdsToFrontendResult> result);
void OnResult(bool result, base::OnceCallback<void(bool)> callback); void OnResult(bool result, base::OnceCallback<void(bool)> callback);
// Weak pointer is fine here since it must outlive this web controller, which
// is guaranteed by the owner of this object.
content::WebContents* web_contents_;
std::unique_ptr<DevtoolsClient> devtools_client_; std::unique_ptr<DevtoolsClient> devtools_client_;
base::WeakPtrFactory<WebController> weak_ptr_factory_; base::WeakPtrFactory<WebController> weak_ptr_factory_;
......
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