blob: 8fa652c3c01dcc7434b5357b84667c5d05207bc6 [file] [log] [blame]
// Copyright 2020 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 <tuple>
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/loader/subresource_redirect_util.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/style_image.h"
#include "third_party/blink/renderer/core/testing/sim/sim_compositor.h"
#include "third_party/blink/renderer/core/testing/sim/sim_request.h"
#include "third_party/blink/renderer/core/testing/sim/sim_test.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include "third_party/blink/renderer/platform/network/network_state_notifier.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
namespace blink {
namespace {
Vector<char> ReadTestImage() {
return test::ReadFromFile(test::CoreTestDataPath("notifications/500x500.png"))
->CopyAs<Vector<char>>();
}
class SubresourceRedirectSimTest
: public ::testing::WithParamInterface<std::tuple<bool, bool, bool>>,
public SimTest {
protected:
SubresourceRedirectSimTest()
: scoped_lazy_image_loading_for_test_(is_lazyload_image_enabled()),
scoped_automatic_lazy_image_loading_for_test_(
is_lazyload_image_enabled()) {
if (is_subresource_redirect_enabled())
scoped_feature_list_.InitAndEnableFeature(features::kSubresourceRedirect);
GetNetworkStateNotifier().SetSaveDataEnabled(is_save_data_enabled());
}
bool is_subresource_redirect_enabled() { return std::get<0>(GetParam()); }
bool is_lazyload_image_enabled() { return std::get<1>(GetParam()); }
bool is_save_data_enabled() { return std::get<2>(GetParam()); }
void LoadMainResource(const String& html_body) {
SimRequest main_resource("https://example.com/", "text/html");
LoadURL("https://example.com/");
main_resource.Complete(html_body);
GetDocument().UpdateStyleAndLayoutTree();
}
// Loads the main page resource and then loads the given image in the page.
void LoadMainResourceAndImage(const String& html_body,
const String& img_url) {
SimRequest image_resource(img_url, "image/png");
LoadMainResource(html_body);
if (!is_lazyload_image_enabled())
image_resource.Complete(ReadTestImage());
Compositor().BeginFrame();
test::RunPendingTasks();
if (is_lazyload_image_enabled()) {
// Scroll down until the image is visible.
GetDocument().View()->LayoutViewport()->SetScrollOffset(
ScrollOffset(0, 10000), mojom::blink::ScrollType::kProgrammatic);
Compositor().BeginFrame();
test::RunPendingTasks();
image_resource.Complete(ReadTestImage());
}
}
// Verifies previews state for the fetched request URL.
void VerifySubresourceRedirectPreviewsState(
const String& url,
bool is_subresource_redirect_allowed) {
PreviewsState previews_state = GetDocument()
.Fetcher()
->CachedResource(KURL(url))
->GetResourceRequest()
.GetPreviewsState();
EXPECT_EQ(is_subresource_redirect_allowed,
(previews_state & PreviewsTypes::kSubresourceRedirectOn) != 0);
}
ScopedLazyImageLoadingForTest scoped_lazy_image_loading_for_test_;
ScopedAutomaticLazyImageLoadingForTest
scoped_automatic_lazy_image_loading_for_test_;
base::test::ScopedFeatureList scoped_feature_list_;
base::HistogramTester histogram_tester_;
};
// This test verifies subresource redirect previews state based on different
// states of SaveData, LazyLoad, SubresourceRedirect features.
TEST_P(SubresourceRedirectSimTest, CSSBackgroundImage) {
LoadMainResourceAndImage(R"HTML(
<style>
#deferred_image {
height:200px;
background-image: url('img.png');
}
</style>
<div style='height:10000px;'></div>
<div id="deferred_image"></div>
)HTML",
"https://example.com/img.png");
// Subresource redirect previews bit should be set only if SaveData and
// SubresourceRedirect feature are enabled.
VerifySubresourceRedirectPreviewsState(
"https://example.com/img.png",
is_save_data_enabled() && is_subresource_redirect_enabled());
}
TEST_P(SubresourceRedirectSimTest, ImgElement) {
LoadMainResourceAndImage(R"HTML(
<body>
<img src='https://example.com/img.png' loading='lazy'/>
</body>
)HTML",
"https://example.com/img.png");
// Subresource redirect previews bit should be set only if SaveData and
// SubresourceRedirect feature are enabled.
VerifySubresourceRedirectPreviewsState(
"https://example.com/img.png",
is_save_data_enabled() && is_subresource_redirect_enabled());
histogram_tester_.ExpectTotalCount("SubresourceRedirect.Blink.Ineligibility",
0);
}
TEST_P(SubresourceRedirectSimTest, JavascriptCreatedSameOriginImage) {
LoadMainResourceAndImage(R"HTML(
<body>
<div></div>
<script>
var img = document.createElement("img");
img.loading = 'lazy';
img.src = 'https://example.com/img.png';
document.getElementsByTagName('div')[0].appendChild(img);
</script>
</body>
)HTML",
"https://example.com/img.png");
VerifySubresourceRedirectPreviewsState("https://example.com/img.png", false);
if (is_save_data_enabled() && is_subresource_redirect_enabled()) {
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.Blink.Ineligibility",
BlinkSubresourceRedirectIneligibility::kJavascriptCreatedSameOrigin,
is_lazyload_image_enabled() ? 2 : 1);
} else {
histogram_tester_.ExpectTotalCount(
"SubresourceRedirect.Blink.Ineligibility", 0);
}
}
TEST_P(SubresourceRedirectSimTest, JavascriptCreatedCrossOriginImage) {
LoadMainResourceAndImage(R"HTML(
<body>
<div></div>
<script>
var img = document.createElement("img");
img.loading = 'lazy';
img.src = 'https://differentorigin.com/img.png';
document.getElementsByTagName('div')[0].appendChild(img);
</script>
</body>
)HTML",
"https://differentorigin.com/img.png");
VerifySubresourceRedirectPreviewsState("https://differentorigin.com/img.png",
false);
if (is_save_data_enabled() && is_subresource_redirect_enabled()) {
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.Blink.Ineligibility",
BlinkSubresourceRedirectIneligibility::kJavascriptCreatedCrossOrigin,
is_lazyload_image_enabled() ? 2 : 1);
} else {
histogram_tester_.ExpectTotalCount(
"SubresourceRedirect.Blink.Ineligibility", 0);
}
}
TEST_P(SubresourceRedirectSimTest, ImgElementWithCrossOriginAttribute) {
LoadMainResourceAndImage(R"HTML(
<body>
<img src='https://example.com/img.png' loading='lazy' crossorigin='anonymous'/>
</body>
)HTML",
"https://example.com/img.png");
VerifySubresourceRedirectPreviewsState("https://example.com/img.png", false);
if (is_save_data_enabled() && is_subresource_redirect_enabled()) {
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.Blink.Ineligibility",
BlinkSubresourceRedirectIneligibility::kCrossOriginAttributeSet,
is_lazyload_image_enabled() ? 2 : 1);
} else {
histogram_tester_.ExpectTotalCount(
"SubresourceRedirect.Blink.Ineligibility", 0);
}
}
TEST_P(SubresourceRedirectSimTest,
RestrictedByContentSecurityPolicyDefaultSrc) {
LoadMainResourceAndImage(R"HTML(
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
</head>
<body>
<img src='https://example.com/img.png' loading='lazy'/>
</body>
)HTML",
"https://example.com/img.png");
VerifySubresourceRedirectPreviewsState("https://example.com/img.png", false);
if (is_save_data_enabled() && is_subresource_redirect_enabled()) {
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.Blink.Ineligibility",
BlinkSubresourceRedirectIneligibility::
kContentSecurityPolicyDefaultSrcRestricted,
is_lazyload_image_enabled() ? 2 : 1);
} else {
histogram_tester_.ExpectTotalCount(
"SubresourceRedirect.Blink.Ineligibility", 0);
}
}
TEST_P(SubresourceRedirectSimTest, RestrictedByContentSecurityPolicyImgSrc) {
LoadMainResourceAndImage(R"HTML(
<head>
<meta http-equiv="Content-Security-Policy" content="img-src 'self'">
</head>
<body>
<img src='https://example.com/img.png' loading='lazy'/>
</body>
)HTML",
"https://example.com/img.png");
VerifySubresourceRedirectPreviewsState("https://example.com/img.png", false);
if (is_save_data_enabled() && is_subresource_redirect_enabled()) {
histogram_tester_.ExpectUniqueSample(
"SubresourceRedirect.Blink.Ineligibility",
BlinkSubresourceRedirectIneligibility::
kContentSecurityPolicyImgSrcRestricted,
is_lazyload_image_enabled() ? 2 : 1);
} else {
histogram_tester_.ExpectTotalCount(
"SubresourceRedirect.Blink.Ineligibility", 0);
}
}
INSTANTIATE_TEST_SUITE_P(
All,
SubresourceRedirectSimTest,
::testing::Combine(::testing::Bool(), /* is_subresource_redirect_enabled */
::testing::Bool(), /* is_lazyload_image_enabled */
::testing::Bool()) /* is_save_data_enabled_*/);
} // namespace
} // namespace blink