Commit 64c8c30f authored by Jordan Demeulenaere's avatar Jordan Demeulenaere Committed by Commit Bot

[Autofill Assistant] Replace the pick_one filter by nth_match.

This CL replaces the pick_one Selector filter by the nth_match filter,
that matches the nth element of the elements currently matching.

See [1] for more background.

[1] https://docs.google.com/document/d/167LaI_WQNr31wBuO3DNlCPD2sx3If2GOHpPsT4atXIk

Change-Id: I3024239d256201fe604c0c72b4985a9de3d45201
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2517535
Commit-Queue: Jordan Demeulenaere <jdemeulenaere@chromium.org>
Reviewed-by: default avatarStephane Zermatten <szermatt@chromium.org>
Reviewed-by: default avatarSandro Maggi <sandromaggi@google.com>
Cr-Commit-Position: refs/heads/master@{#824854}
parent df9d6f5c
...@@ -251,8 +251,8 @@ public class AutofillAssistantKeyboardIntegrationTest { ...@@ -251,8 +251,8 @@ public class AutofillAssistantKeyboardIntegrationTest {
SelectorProto element = SelectorProto element =
(SelectorProto) SelectorProto.newBuilder() (SelectorProto) SelectorProto.newBuilder()
.addFilters(SelectorProto.Filter.newBuilder().setCssSelector("#iframe")) .addFilters(SelectorProto.Filter.newBuilder().setCssSelector("#iframe"))
.addFilters(SelectorProto.Filter.newBuilder().setPickOne( .addFilters(SelectorProto.Filter.newBuilder().setNthMatch(
SelectorProto.EmptyFilter.getDefaultInstance())) SelectorProto.NthMatchFilter.newBuilder().setIndex(0)))
.addFilters(SelectorProto.Filter.newBuilder().setEnterFrame( .addFilters(SelectorProto.Filter.newBuilder().setEnterFrame(
SelectorProto.EmptyFilter.getDefaultInstance())) SelectorProto.EmptyFilter.getDefaultInstance()))
.addFilters(SelectorProto.Filter.newBuilder().setCssSelector("#name")) .addFilters(SelectorProto.Filter.newBuilder().setCssSelector("#name"))
......
...@@ -255,8 +255,8 @@ public class AutofillAssistantOverlayIntegrationTest { ...@@ -255,8 +255,8 @@ public class AutofillAssistantOverlayIntegrationTest {
SelectorProto element = SelectorProto element =
(SelectorProto) SelectorProto.newBuilder() (SelectorProto) SelectorProto.newBuilder()
.addFilters(SelectorProto.Filter.newBuilder().setCssSelector("#iframe")) .addFilters(SelectorProto.Filter.newBuilder().setCssSelector("#iframe"))
.addFilters(SelectorProto.Filter.newBuilder().setPickOne( .addFilters(SelectorProto.Filter.newBuilder().setNthMatch(
SelectorProto.EmptyFilter.getDefaultInstance())) SelectorProto.NthMatchFilter.newBuilder().setIndex(0)))
.addFilters(SelectorProto.Filter.newBuilder().setEnterFrame( .addFilters(SelectorProto.Filter.newBuilder().setEnterFrame(
SelectorProto.EmptyFilter.getDefaultInstance())) SelectorProto.EmptyFilter.getDefaultInstance()))
...@@ -310,8 +310,8 @@ public class AutofillAssistantOverlayIntegrationTest { ...@@ -310,8 +310,8 @@ public class AutofillAssistantOverlayIntegrationTest {
SelectorProto element = SelectorProto element =
(SelectorProto) SelectorProto.newBuilder() (SelectorProto) SelectorProto.newBuilder()
.addFilters(SelectorProto.Filter.newBuilder().setCssSelector("#iframe")) .addFilters(SelectorProto.Filter.newBuilder().setCssSelector("#iframe"))
.addFilters(SelectorProto.Filter.newBuilder().setPickOne( .addFilters(SelectorProto.Filter.newBuilder().setNthMatch(
SelectorProto.EmptyFilter.getDefaultInstance())) SelectorProto.NthMatchFilter.newBuilder().setIndex(0)))
.addFilters(SelectorProto.Filter.newBuilder().setEnterFrame( .addFilters(SelectorProto.Filter.newBuilder().setEnterFrame(
SelectorProto.EmptyFilter.getDefaultInstance())) SelectorProto.EmptyFilter.getDefaultInstance()))
......
...@@ -71,7 +71,6 @@ bool operator<(const SelectorProto::Filter& a, const SelectorProto::Filter& b) { ...@@ -71,7 +71,6 @@ bool operator<(const SelectorProto::Filter& a, const SelectorProto::Filter& b) {
case SelectorProto::Filter::kBoundingBox: case SelectorProto::Filter::kBoundingBox:
case SelectorProto::Filter::kEnterFrame: case SelectorProto::Filter::kEnterFrame:
case SelectorProto::Filter::kPickOne:
case SelectorProto::Filter::kLabelled: case SelectorProto::Filter::kLabelled:
return false; return false;
...@@ -85,6 +84,9 @@ bool operator<(const SelectorProto::Filter& a, const SelectorProto::Filter& b) { ...@@ -85,6 +84,9 @@ bool operator<(const SelectorProto::Filter& a, const SelectorProto::Filter& b) {
case SelectorProto::Filter::kMatchCssSelector: case SelectorProto::Filter::kMatchCssSelector:
return a.match_css_selector() < b.match_css_selector(); return a.match_css_selector() < b.match_css_selector();
case SelectorProto::Filter::kNthMatch:
return a.nth_match().index() < b.nth_match().index();
case SelectorProto::Filter::FILTER_NOT_SET: case SelectorProto::Filter::FILTER_NOT_SET:
return false; return false;
} }
...@@ -100,7 +102,7 @@ SelectorProto ToSelectorProto(const std::vector<std::string>& s) { ...@@ -100,7 +102,7 @@ SelectorProto ToSelectorProto(const std::vector<std::string>& s) {
if (!s.empty()) { if (!s.empty()) {
for (size_t i = 0; i < s.size(); i++) { for (size_t i = 0; i < s.size(); i++) {
if (i > 0) { if (i > 0) {
proto.add_filters()->mutable_pick_one(); proto.add_filters()->mutable_nth_match()->set_index(0);
proto.add_filters()->mutable_enter_frame(); proto.add_filters()->mutable_enter_frame();
} }
proto.add_filters()->set_css_selector(s[i]); proto.add_filters()->set_css_selector(s[i]);
...@@ -226,10 +228,11 @@ base::Optional<std::string> Selector::ExtractSingleCssSelectorForAutofill() ...@@ -226,10 +228,11 @@ base::Optional<std::string> Selector::ExtractSingleCssSelectorForAutofill()
break; break;
case SelectorProto::Filter::kBoundingBox: case SelectorProto::Filter::kBoundingBox:
case SelectorProto::Filter::kPickOne: case SelectorProto::Filter::kNthMatch:
// Ignore these; they're not relevant for the autofill use-case if (filter.nth_match().index() == 0)
break; break;
FALLTHROUGH;
case SelectorProto::Filter::kInnerText: case SelectorProto::Filter::kInnerText:
case SelectorProto::Filter::kValue: case SelectorProto::Filter::kValue:
case SelectorProto::Filter::kPseudoType: case SelectorProto::Filter::kPseudoType:
...@@ -351,8 +354,8 @@ std::ostream& operator<<(std::ostream& out, const SelectorProto::Filter& f) { ...@@ -351,8 +354,8 @@ std::ostream& operator<<(std::ostream& out, const SelectorProto::Filter& f) {
out << "bounding_box"; out << "bounding_box";
return out; return out;
case SelectorProto::Filter::kPickOne: case SelectorProto::Filter::kNthMatch:
out << "pick_one"; out << "nth_match[" << f.nth_match().index() << "]";
return out; return out;
case SelectorProto::Filter::kLabelled: case SelectorProto::Filter::kLabelled:
......
...@@ -23,10 +23,11 @@ TEST(SelectorTest, Constructor_WithIframe) { ...@@ -23,10 +23,11 @@ TEST(SelectorTest, Constructor_WithIframe) {
Selector selector({"#frame", "#test"}); Selector selector({"#frame", "#test"});
ASSERT_EQ(4, selector.proto.filters().size()); ASSERT_EQ(4, selector.proto.filters().size());
EXPECT_EQ("#frame", selector.proto.filters(0).css_selector()); EXPECT_EQ("#frame", selector.proto.filters(0).css_selector());
EXPECT_EQ(selector.proto.filters(1).filter_case(), EXPECT_EQ(SelectorProto::Filter::kNthMatch,
SelectorProto::Filter::kPickOne); selector.proto.filters(1).filter_case());
EXPECT_EQ(selector.proto.filters(2).filter_case(), EXPECT_EQ(0, selector.proto.filters(1).nth_match().index());
SelectorProto::Filter::kEnterFrame); EXPECT_EQ(SelectorProto::Filter::kEnterFrame,
selector.proto.filters(2).filter_case());
EXPECT_EQ("#test", selector.proto.filters(3).css_selector()); EXPECT_EQ("#test", selector.proto.filters(3).css_selector());
} }
......
...@@ -992,11 +992,9 @@ message SelectorProto { ...@@ -992,11 +992,9 @@ message SelectorProto {
// renamed as having a bounding box is not enough to imply visibility. // renamed as having a bounding box is not enough to imply visibility.
EmptyFilter bounding_box = 6; EmptyFilter bounding_box = 6;
// Pick just one match and continue. Ignore all other matches. // Take the nth match. Fails with ELEMENT_RESOLUTION_FAILED if there are
// // not enough matches.
// For backward-compatibility with older element references, pick_one can NthMatchFilter nth_match = 7;
// be put before enter_frame.
EmptyFilter pick_one = 7;
// Only keep elements that have a pseudo-element with the given content. // Only keep elements that have a pseudo-element with the given content.
// //
...@@ -1208,6 +1206,11 @@ message SelectorProto { ...@@ -1208,6 +1206,11 @@ message SelectorProto {
message EmptyFilter {} message EmptyFilter {}
message NthMatchFilter {
// Take the match at the given |index|.
optional int32 index = 1;
}
reserved 1 to 8; reserved 1 to 8;
} }
......
...@@ -222,7 +222,7 @@ bool ElementFinder::JsFilterBuilder::AddFilter( ...@@ -222,7 +222,7 @@ bool ElementFinder::JsFilterBuilder::AddFilter(
case SelectorProto::Filter::kEnterFrame: case SelectorProto::Filter::kEnterFrame:
case SelectorProto::Filter::kPseudoType: case SelectorProto::Filter::kPseudoType:
case SelectorProto::Filter::kPickOne: case SelectorProto::Filter::kNthMatch:
case SelectorProto::Filter::kClosest: case SelectorProto::Filter::kClosest:
case SelectorProto::Filter::FILTER_NOT_SET: case SelectorProto::Filter::FILTER_NOT_SET:
return false; return false;
...@@ -368,7 +368,7 @@ void ElementFinder::ExecuteNextTask() { ...@@ -368,7 +368,7 @@ void ElementFinder::ExecuteNextTask() {
break; break;
case ResultType::kAnyMatch: case ResultType::kAnyMatch:
if (!ConsumeAnyMatchOrFail(object_id)) { if (!ConsumeMatchAtOrFail(0, object_id)) {
return; return;
} }
break; break;
...@@ -410,9 +410,9 @@ void ElementFinder::ExecuteNextTask() { ...@@ -410,9 +410,9 @@ void ElementFinder::ExecuteNextTask() {
return; return;
} }
case SelectorProto::Filter::kPickOne: { case SelectorProto::Filter::kNthMatch: {
std::string object_id; std::string object_id;
if (!ConsumeAnyMatchOrFail(object_id)) if (!ConsumeMatchAtOrFail(filter.nth_match().index(), object_id))
return; return;
next_filter_index_++; next_filter_index_++;
...@@ -479,9 +479,10 @@ bool ElementFinder::ConsumeOneMatchOrFail(std::string& object_id_out) { ...@@ -479,9 +479,10 @@ bool ElementFinder::ConsumeOneMatchOrFail(std::string& object_id_out) {
return true; return true;
} }
bool ElementFinder::ConsumeAnyMatchOrFail(std::string& object_id_out) { bool ElementFinder::ConsumeMatchAtOrFail(size_t index,
if (current_matches_.size() > 0) { std::string& object_id_out) {
object_id_out = current_matches_[0]; if (index < current_matches_.size()) {
object_id_out = current_matches_[index];
current_matches_.clear(); current_matches_.clear();
return true; return true;
} }
......
...@@ -220,17 +220,11 @@ class ElementFinder : public WebControllerWorker { ...@@ -220,17 +220,11 @@ class ElementFinder : public WebControllerWorker {
// required data is available. // required data is available.
bool ConsumeOneMatchOrFail(std::string& object_id_out); bool ConsumeOneMatchOrFail(std::string& object_id_out);
// Make sure there's at least one match, take one and put it in // Make sure there's at least |index + 1| matches, take the one at that index
// |object_id_out|, then return true. // and put it in |object_id_out|, then return true.
// //
// If there are no matches, send an error response and return false. // If there are not enough matches, send an error response and return false.
// If there are not enough matches yet, fetch them in the background and bool ConsumeMatchAtOrFail(size_t index, std::string& object_id_out);
// return false. This calls ExecuteNextTask() once matches have been fetched.
//
// If this returns true, continue processing. If this returns false, return
// from ExecuteNextTask(). ExecuteNextTask() will be called again once the
// required data is available.
bool ConsumeAnyMatchOrFail(std::string& object_id_out);
// Make sure there's at least one match and move them all into // Make sure there's at least one match and move them all into
// |matches_out|. // |matches_out|.
......
...@@ -1147,7 +1147,7 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, PseudoTypeThenBoundingBox) { ...@@ -1147,7 +1147,7 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, PseudoTypeThenBoundingBox) {
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, PseudoTypeThenPickOne) { IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, PseudoTypeThenPickOne) {
Selector selector({"span"}); Selector selector({"span"});
selector.SetPseudoType(PseudoType::BEFORE); selector.SetPseudoType(PseudoType::BEFORE);
selector.proto.add_filters()->mutable_pick_one(); selector.proto.add_filters()->mutable_nth_match()->set_index(0);
RunStrictElementCheck(selector, true); RunStrictElementCheck(selector, true);
} }
...@@ -2291,7 +2291,7 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, ...@@ -2291,7 +2291,7 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest,
Selector selector({"input"}); Selector selector({"input"});
auto* closest = selector.proto.add_filters()->mutable_closest(); auto* closest = selector.proto.add_filters()->mutable_closest();
closest->add_target()->set_css_selector("#iframe"); closest->add_target()->set_css_selector("#iframe");
closest->add_target()->mutable_pick_one(); closest->add_target()->mutable_nth_match()->set_index(0);
closest->add_target()->mutable_enter_frame(); closest->add_target()->mutable_enter_frame();
closest->add_target()->set_css_selector("div"); closest->add_target()->set_css_selector("div");
...@@ -2435,4 +2435,34 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, OnTopFindsElementInShadow) { ...@@ -2435,4 +2435,34 @@ IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, OnTopFindsElementInShadow) {
RunLaxElementCheck(button, false); RunLaxElementCheck(button, false);
} }
IN_PROC_BROWSER_TEST_F(WebControllerBrowserTest, NthMatch) {
Selector selector;
selector.proto.add_filters()->set_css_selector(".nth_match_parent");
selector.proto.add_filters()->mutable_nth_match()->set_index(1);
selector.proto.add_filters()->set_css_selector(".nth_match_child");
auto* pick_at_filter = selector.proto.add_filters();
std::string element_tag;
pick_at_filter->mutable_nth_match()->set_index(0);
ASSERT_EQ(ACTION_APPLIED,
GetElementTag(selector, &element_tag).proto_status());
EXPECT_EQ("P", element_tag);
pick_at_filter->mutable_nth_match()->set_index(1);
ASSERT_EQ(ACTION_APPLIED,
GetElementTag(selector, &element_tag).proto_status());
EXPECT_EQ("UL", element_tag);
pick_at_filter->mutable_nth_match()->set_index(2);
ASSERT_EQ(ACTION_APPLIED,
GetElementTag(selector, &element_tag).proto_status());
EXPECT_EQ("LI", element_tag);
pick_at_filter->mutable_nth_match()->set_index(3);
ASSERT_EQ(ACTION_APPLIED,
GetElementTag(selector, &element_tag).proto_status());
EXPECT_EQ("STRONG", element_tag);
}
} // namespace autofill_assistant } // namespace autofill_assistant
...@@ -375,5 +375,17 @@ found in the LICENSE file. ...@@ -375,5 +375,17 @@ found in the LICENSE file.
<div id="overlay"> <div id="overlay">
This is an overlay that covers the whole page. This is an overlay that covers the whole page.
</div> </div>
<div class="nth_match_parent">
<span class="nth_match_child"></span>
</div>
<div class="nth_match_parent">
<p class="nth_match_child"></p>
<ul class="nth_match_child">
<li class="nth_match_child"></li>
</ul>
<strong class="nth_match_child"></strong>
</div>
</body> </body>
</html> </html>
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