blob: ebb4c7f575ed9e8e362e7aff47a0d164ea924787 [file] [log] [blame]
Yao Xiaoc646d0b2020-01-31 22:30:591// Copyright 2020 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
6#include "chrome/test/base/ui_test_utils.h"
7#include "components/page_load_metrics/browser/page_load_metrics_test_waiter.h"
8#include "components/subresource_filter/core/common/test_ruleset_utils.h"
9#include "content/public/test/test_navigation_observer.h"
10#include "net/dns/mock_host_resolver.h"
11#include "third_party/blink/public/common/features.h"
12
13namespace {
14
15enum class StickyType {
16 kReposition,
17 kFixedPosition,
18 kStickyPosition,
19 kNotSticky,
20};
21
22base::StringPiece StickyTypeToString(StickyType type) {
23 switch (type) {
24 case StickyType::kReposition:
25 return "reposition";
26 case StickyType::kFixedPosition:
27 return "fixed_position";
28 case StickyType::kStickyPosition:
29 return "sticky_position";
30 case StickyType::kNotSticky:
31 return "not_sticky";
32 }
33}
34
35} // namespace
36
37class LargeStickyAdBrowserTest
38 : public subresource_filter::SubresourceFilterBrowserTest {
39 public:
40 ~LargeStickyAdBrowserTest() override = default;
41
42 void SetUpOnMainThread() override {
43 host_resolver()->AddRule("*", "127.0.0.1");
44 SetRulesetWithRules(
45 {subresource_filter::testing::CreateSuffixRule("title1.html")});
46 embedded_test_server()->ServeFilesFromSourceDirectory("chrome/test/data");
47 content::SetupCrossSiteRedirector(embedded_test_server());
48 ASSERT_TRUE(embedded_test_server()->Start());
49 }
50
51 void ExecuteScriptOnFrame(const std::string& script,
52 content::RenderFrameHost* rfh) {
53 rfh->ExecuteJavaScriptForTests(base::ASCIIToUTF16(script),
54 base::NullCallback());
55 }
56
57 content::RenderFrameHost* AppendChildToFrame(const std::string& frame_id,
58 const GURL& subframe_url,
59 StickyType sticky_type,
60 double frame_height_ratio,
61 content::RenderFrameHost* rfh) {
62 content::TestNavigationObserver navigation_observer(web_contents());
63 std::string sticky_type_str = StickyTypeToString(sticky_type).data();
64 ExecuteScriptOnFrame(
65 base::StringPrintf("appendFrame('%s','%s','%s','%f')", frame_id.c_str(),
66 subframe_url.spec().c_str(), sticky_type_str.c_str(),
67 frame_height_ratio),
68 rfh);
69 navigation_observer.Wait();
70
71 return content::FrameMatchingPredicate(
72 web_contents(),
73 base::BindRepeating(&content::FrameMatchesName, frame_id));
74 }
75};
76
77class LargeStickyAdBrowserTestStickyChildAd
78 : public LargeStickyAdBrowserTest,
79 public ::testing::WithParamInterface<
80 std::tuple<StickyType, bool /* remote_subframe */>> {};
81
82// This test suite tests sticky child frame achieved by re-position, fixed
83// position, and sticky position. The test is also parameterized by whether the
84// sticky frame is local or remote to the top frame.
85IN_PROC_BROWSER_TEST_P(LargeStickyAdBrowserTestStickyChildAd,
86 StickyAdDetected) {
87 bool remote_subframe;
88 StickyType sticky_type;
89 std::tie(sticky_type, remote_subframe) = GetParam();
90
91 page_load_metrics::PageLoadMetricsTestWaiter web_feature_waiter(
92 web_contents());
93
94 std::string host1 = "foo.com";
95 std::string host2 = remote_subframe ? "bar.com" : host1;
96
97 GURL top_frame_url =
98 embedded_test_server()->GetURL(host1, "/sticky_frame_test_factory.html");
99 ui_test_utils::NavigateToURL(browser(), top_frame_url);
100
101 GURL subframe_url = embedded_test_server()->GetURL(host2, "/title1.html");
102
103 ExecuteScriptOnFrame("appendSmallParagraph()",
104 web_contents()->GetMainFrame());
105 AppendChildToFrame("frame_id", subframe_url, sticky_type, 0.5,
106 web_contents()->GetMainFrame());
107 ExecuteScriptOnFrame("appendLargeParagraph()",
108 web_contents()->GetMainFrame());
109 ExecuteScriptOnFrame("autoScrollUp()", web_contents()->GetMainFrame());
110
111 web_feature_waiter.AddWebFeatureExpectation(
112 blink::mojom::WebFeature::kLargeStickyAd);
113
114 web_feature_waiter.Wait();
115}
116
117INSTANTIATE_TEST_SUITE_P(
118 /* no prefix */,
119 LargeStickyAdBrowserTestStickyChildAd,
120 ::testing::Combine(::testing::Values(StickyType::kReposition,
121 StickyType::kFixedPosition,
122 StickyType::kStickyPosition),
123 ::testing::Bool()));
124
125class LargeStickyAdBrowserTestFixedChildDefaultGrandchildAd
126 : public LargeStickyAdBrowserTest,
127 public ::testing::WithParamInterface<
128 std::tuple<bool /* remote_subframe */,
129 bool /* remote_grandchild */>> {};
130
131// Test a A(B(C)) setup where B is a large fixed-positioned non-ad frame, and C
132// is an large default-positioned ad frame. Content in A keeps scrolling. C
133// should be determined to be a large sticky ad.
134//
135// When A and B are remote in particular, this tests the scenario where when
136// page A is scrolling, there's no animation frame in B, and normally no
137// rendering / intersection-observation will be scheduled in B. However, the
138// sticky frame tracking logic should force the rendering in B so that frame C
139// can be continuously tracked while scrolling.
140IN_PROC_BROWSER_TEST_P(LargeStickyAdBrowserTestFixedChildDefaultGrandchildAd,
141 StickyAdDetected) {
142 bool remote_subframe;
143 bool remote_grandchild;
144 std::tie(remote_subframe, remote_grandchild) = GetParam();
145
146 if (remote_subframe &&
147 !base::FeatureList::IsEnabled(
148 blink::features::kForceExtraRenderingToTrackStickyFrame)) {
149 return;
150 }
151
152 std::string host1 = "foo.com";
153 std::string host2 = remote_subframe ? "bar.com" : host1;
154 std::string host3 = remote_grandchild ? "baz.com" : host2;
155
156 page_load_metrics::PageLoadMetricsTestWaiter web_feature_waiter(
157 web_contents());
158
159 GURL top_frame_url =
160 embedded_test_server()->GetURL(host1, "/sticky_frame_test_factory.html");
161 ui_test_utils::NavigateToURL(browser(), top_frame_url);
162
163 GURL subframe_url =
164 embedded_test_server()->GetURL(host2, "/sticky_frame_test_factory.html");
165 GURL grandchild_url = embedded_test_server()->GetURL(host3, "/title1.html");
166
167 ExecuteScriptOnFrame("appendSmallParagraph()",
168 web_contents()->GetMainFrame());
169 content::RenderFrameHost* child_1 =
170 AppendChildToFrame("frame_1", subframe_url, StickyType::kFixedPosition,
171 0.6, web_contents()->GetMainFrame());
172 AppendChildToFrame("frame_2", grandchild_url, StickyType::kNotSticky, 0.6,
173 child_1);
174 ExecuteScriptOnFrame("appendLargeParagraph()",
175 web_contents()->GetMainFrame());
176 ExecuteScriptOnFrame("autoScrollUp()", web_contents()->GetMainFrame());
177
178 web_feature_waiter.AddWebFeatureExpectation(
179 blink::mojom::WebFeature::kLargeStickyAd);
180
181 web_feature_waiter.Wait();
182}
183
184INSTANTIATE_TEST_SUITE_P(
185 /* no prefix */,
186 LargeStickyAdBrowserTestFixedChildDefaultGrandchildAd,
187 ::testing::Combine(::testing::Bool(), ::testing::Bool()));
188
189class LargeStickyAdBrowserTestScrollingTopPageReverseMovingGrandchildAd
190 : public LargeStickyAdBrowserTest,
191 public ::testing::WithParamInterface<
192 std::tuple<bool /* remote_subframe */,
193 bool /* remote_grandchild */>> {};
194
195// Test a A(B(C)) setup. B is being scrolled up as the top page scrolls, and C
196// is being positioned downward relative to B at the same speed. The total
197// effect is that C is sticky to the browser viewport. The test is parameterized
198// by whether the B is local or remote to A and whether C is local to B (or
199// remote to both A and B).
200IN_PROC_BROWSER_TEST_P(
201 LargeStickyAdBrowserTestScrollingTopPageReverseMovingGrandchildAd,
202 StickyAdDetected) {
203 bool remote_subframe;
204 bool remote_grandchild;
205 std::tie(remote_subframe, remote_grandchild) = GetParam();
206
207 page_load_metrics::PageLoadMetricsTestWaiter web_feature_waiter(
208 web_contents());
209
210 std::string host1 = "foo.com";
211 std::string host2 = remote_subframe ? "bar.com" : host1;
212 std::string host3 = remote_grandchild ? "baz.com" : host2;
213
214 GURL top_frame_url =
215 embedded_test_server()->GetURL(host1, "/sticky_frame_test_factory.html");
216 GURL subframe_url =
217 embedded_test_server()->GetURL(host2, "/sticky_frame_test_factory.html");
218 GURL grandchild_frame_url =
219 embedded_test_server()->GetURL(host3, "/title1.html");
220
221 ui_test_utils::NavigateToURL(browser(), top_frame_url);
222
223 ExecuteScriptOnFrame("appendSmallParagraph()",
224 web_contents()->GetMainFrame());
225 content::RenderFrameHost* child1 =
226 AppendChildToFrame("child1", subframe_url, StickyType::kNotSticky, 0.8,
227 web_contents()->GetMainFrame());
228 AppendChildToFrame("child2", grandchild_frame_url, StickyType::kFixedPosition,
229 0.5, child1);
230 ExecuteScriptOnFrame("appendLargeParagraph()",
231 web_contents()->GetMainFrame());
232
233 ExecuteScriptOnFrame("autoMoveFirstIframeDown()", child1);
234 ExecuteScriptOnFrame("autoScrollUp()", web_contents()->GetMainFrame());
235
236 web_feature_waiter.AddWebFeatureExpectation(
237 blink::mojom::WebFeature::kLargeStickyAd);
238
239 web_feature_waiter.Wait();
240}
241
242INSTANTIATE_TEST_SUITE_P(
243 /* no prefix */,
244 LargeStickyAdBrowserTestScrollingTopPageReverseMovingGrandchildAd,
245 ::testing::Combine(::testing::Bool(), ::testing::Bool()));