blob: fff74cd93db7ee6d31ff9839382f647d4330151a [file] [log] [blame]
// 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 "third_party/blink/renderer/core/loader/previews_resource_loading_hints.h"
#include <memory>
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "components/ukm/test_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/loader/frame_loader.h"
#include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/geometry/int_size.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_load_priority.h"
#include "third_party/blink/renderer/platform/testing/url_test_helpers.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
namespace {
class PreviewsResourceLoadingHintsTest : public PageTestBase {
public:
PreviewsResourceLoadingHintsTest() {
dummy_page_holder_ = std::make_unique<DummyPageHolder>(IntSize(1, 1));
}
protected:
std::unique_ptr<DummyPageHolder> dummy_page_holder_;
};
TEST_F(PreviewsResourceLoadingHintsTest, NoPatterns) {
Vector<WTF::String> subresources_to_block;
PreviewsResourceLoadingHints* hints = PreviewsResourceLoadingHints::Create(
*dummy_page_holder_->GetFrame().DomWindow(),
ukm::UkmRecorder::GetNewSourceID(), subresources_to_block);
EXPECT_TRUE(hints->AllowLoad(ResourceType::kScript,
KURL("https://www.example.com/"),
ResourceLoadPriority::kHighest));
}
TEST_F(PreviewsResourceLoadingHintsTest, OnePattern) {
Vector<WTF::String> subresources_to_block;
subresources_to_block.push_back("foo.jpg");
PreviewsResourceLoadingHints* hints = PreviewsResourceLoadingHints::Create(
*dummy_page_holder_->GetFrame().DomWindow(),
ukm::UkmRecorder::GetNewSourceID(), subresources_to_block);
const struct {
KURL url;
bool allow_load_expected;
} tests[] = {
{KURL("https://www.example.com/"), true},
{KURL("https://www.example.com/foo.js"), true},
{KURL("https://www.example.com/foo.jpg"), false},
{KURL("https://www.example.com/pages/foo.jpg"), false},
{KURL("https://www.example.com/foobar.jpg"), true},
{KURL("https://www.example.com/barfoo.jpg"), false},
{KURL("http://www.example.com/foo.jpg"), false},
{KURL("http://www.example.com/foo.jpg?q=alpha"), false},
{KURL("http://www.example.com/bar.jpg?q=foo.jpg"), true},
{KURL("http://www.example.com/bar.jpg?q=foo.jpg#foo.jpg"), true},
};
for (const auto& test : tests) {
base::HistogramTester histogram_tester;
// By default, resource blocking hints do not apply to images.
EXPECT_TRUE(hints->AllowLoad(ResourceType::kImage, test.url,
ResourceLoadPriority::kHighest));
// By default, resource blocking hints apply to CSS and Scripts.
EXPECT_EQ(test.allow_load_expected,
hints->AllowLoad(ResourceType::kCSSStyleSheet, test.url,
ResourceLoadPriority::kHighest));
EXPECT_EQ(test.allow_load_expected,
hints->AllowLoad(ResourceType::kScript, test.url,
ResourceLoadPriority::kHighest));
histogram_tester.ExpectUniqueSample(
"ResourceLoadingHints.ResourceLoadingBlocked",
!test.allow_load_expected, 2);
if (!test.allow_load_expected) {
histogram_tester.ExpectUniqueSample(
"ResourceLoadingHints.ResourceLoadingBlocked.ResourceLoadPriority."
"Blocked",
ResourceLoadPriority::kHighest, 2);
histogram_tester.ExpectTotalCount(
"ResourceLoadingHints.ResourceLoadingBlocked.ResourceLoadPriority."
"Allowed",
0);
} else {
histogram_tester.ExpectTotalCount(
"ResourceLoadingHints.ResourceLoadingBlocked.ResourceLoadPriority."
"Blocked",
0);
histogram_tester.ExpectUniqueSample(
"ResourceLoadingHints.ResourceLoadingBlocked.ResourceLoadPriority."
"Allowed",
ResourceLoadPriority::kHighest, 2);
}
}
}
TEST_F(PreviewsResourceLoadingHintsTest, MultiplePatterns) {
Vector<WTF::String> subresources_to_block;
subresources_to_block.push_back(".example1.com/foo.jpg");
subresources_to_block.push_back(".example1.com/bar.jpg");
subresources_to_block.push_back(".example2.com/baz.jpg");
PreviewsResourceLoadingHints* hints = PreviewsResourceLoadingHints::Create(
*dummy_page_holder_->GetFrame().DomWindow(),
ukm::UkmRecorder::GetNewSourceID(), subresources_to_block);
const struct {
KURL url;
bool allow_load_expected;
} tests[] = {
{KURL("https://www.example1.com/"), true},
{KURL("https://www.example1.com/foo.js"), true},
{KURL("https://www.example1.com/foo.jpg"), false},
{KURL("https://www.example1.com/pages/foo.jpg"), true},
{KURL("https://www.example1.com/foobar.jpg"), true},
{KURL("https://www.example1.com/barfoo.jpg"), true},
{KURL("http://www.example1.com/foo.jpg"), false},
{KURL("http://www.example1.com/bar.jpg"), false},
{KURL("http://www.example2.com/baz.jpg"), false},
{KURL("http://www.example2.com/pages/baz.jpg"), true},
{KURL("http://www.example2.com/baz.html"), true},
};
for (const auto& test : tests) {
EXPECT_EQ(test.allow_load_expected,
hints->AllowLoad(ResourceType::kScript, test.url,
ResourceLoadPriority::kHighest));
}
}
TEST_F(PreviewsResourceLoadingHintsTest, OnePatternHistogramChecker) {
Vector<WTF::String> subresources_to_block;
subresources_to_block.push_back("foo.jpg");
PreviewsResourceLoadingHints* hints = PreviewsResourceLoadingHints::Create(
*dummy_page_holder_->GetFrame().DomWindow(),
ukm::UkmRecorder::GetNewSourceID(), subresources_to_block);
const struct {
KURL url;
bool allow_load_expected;
ResourceLoadPriority resource_load_priority;
} tests[] = {
{KURL("https://www.example.com/foo.js"), true,
ResourceLoadPriority::kLow},
{KURL("https://www.example.com/foo.jpg"), false,
ResourceLoadPriority::kLow},
{KURL("https://www.example.com/foo.js"), true,
ResourceLoadPriority::kMedium},
{KURL("https://www.example.com/foo.jpg"), false,
ResourceLoadPriority::kMedium},
};
for (const auto& test : tests) {
base::HistogramTester histogram_tester;
EXPECT_EQ(test.allow_load_expected,
hints->AllowLoad(ResourceType::kScript, test.url,
test.resource_load_priority));
histogram_tester.ExpectUniqueSample(
"ResourceLoadingHints.ResourceLoadingBlocked",
!test.allow_load_expected, 1);
if (!test.allow_load_expected) {
histogram_tester.ExpectUniqueSample(
"ResourceLoadingHints.ResourceLoadingBlocked.ResourceLoadPriority."
"Blocked",
test.resource_load_priority, 1);
histogram_tester.ExpectTotalCount(
"ResourceLoadingHints.ResourceLoadingBlocked.ResourceLoadPriority."
"Allowed",
0);
} else {
histogram_tester.ExpectTotalCount(
"ResourceLoadingHints.ResourceLoadingBlocked.ResourceLoadPriority."
"Blocked",
0);
histogram_tester.ExpectUniqueSample(
"ResourceLoadingHints.ResourceLoadingBlocked.ResourceLoadPriority."
"Allowed",
test.resource_load_priority, 1);
}
}
}
TEST_F(PreviewsResourceLoadingHintsTest, MultiplePatternUKMChecker) {
Vector<WTF::String> subresources_to_block;
subresources_to_block.push_back(".example1.com/low_1.jpg");
subresources_to_block.push_back(".example1.com/very_low_1.jpg");
subresources_to_block.push_back(".example1.com/very_high_1.jpg");
subresources_to_block.push_back(".example1.com/medium_1_and_medium_4.jpg");
subresources_to_block.push_back(".example1.com/unused_1.jpg");
subresources_to_block.push_back(".example2.com/medium_2.jpg");
subresources_to_block.push_back(".example2.com/unused_2.jpg");
subresources_to_block.push_back(".example3.com/unused_3.jpg");
subresources_to_block.push_back(".example3.com/very_low_2_and_medium_3.jpg");
PreviewsResourceLoadingHints* hints = PreviewsResourceLoadingHints::Create(
*dummy_page_holder_->GetFrame().DomWindow(),
ukm::UkmRecorder::GetNewSourceID(), subresources_to_block);
const struct {
KURL url;
ResourceLoadPriority resource_load_priority;
} resources_to_load[] = {
{KURL("https://www.example1.com/"), ResourceLoadPriority::kHigh},
{KURL("https://www.example1.com/foo.js"), ResourceLoadPriority::kLow},
{KURL("https://www.example1.com/very_low_1.jpg"),
ResourceLoadPriority::kVeryLow},
{KURL("https://www.example1.com/low_1.jpg"), ResourceLoadPriority::kLow},
{KURL("https://www.example1.com/very_high_1.jpg"),
ResourceLoadPriority::kVeryHigh},
{KURL("https://www.example1.com/pages/foo.jpg"),
ResourceLoadPriority::kVeryLow},
{KURL("https://www.example1.com/foobar.jpg"),
ResourceLoadPriority::kVeryHigh},
{KURL("https://www.example1.com/barfoo.jpg"),
ResourceLoadPriority::kVeryHigh},
{KURL("http://www.example1.com/foo.jpg"), ResourceLoadPriority::kLow},
{KURL("http://www.example1.com/medium_1_and_medium_4.jpg"),
ResourceLoadPriority::kMedium},
{KURL("http://www.example2.com/medium_2.jpg"),
ResourceLoadPriority::kMedium},
{KURL("http://www.example2.com/pages/baz.jpg"),
ResourceLoadPriority::kLow},
{KURL("http://www.example2.com/baz.html"),
ResourceLoadPriority::kVeryHigh},
{KURL("http://www.example3.com/very_low_2_and_medium_3.jpg"),
ResourceLoadPriority::kVeryLow},
{KURL("http://www.example3.com/very_low_2_and_medium_3.jpg"),
ResourceLoadPriority::kMedium},
{KURL("http://www.example1.com/medium_1_and_medium_4.jpg"),
ResourceLoadPriority::kMedium},
};
for (const auto& resource_to_load : resources_to_load) {
hints->AllowLoad(ResourceType::kScript, resource_to_load.url,
resource_to_load.resource_load_priority);
}
ukm::TestAutoSetUkmRecorder test_ukm_recorder;
hints->RecordUKM(&test_ukm_recorder);
using UkmEntry = ukm::builders::PreviewsResourceLoadingHints;
auto entries = test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName);
ASSERT_EQ(1u, entries.size());
const auto* entry = entries[0];
test_ukm_recorder.ExpectEntryMetric(
entry, UkmEntry::kpatterns_to_block_totalName, 9);
test_ukm_recorder.ExpectEntryMetric(entry,
UkmEntry::kpatterns_to_block_usedName, 6);
test_ukm_recorder.ExpectEntryMetric(
entry, UkmEntry::kblocked_very_low_priorityName, 2);
test_ukm_recorder.ExpectEntryMetric(entry,
UkmEntry::kblocked_low_priorityName, 1);
test_ukm_recorder.ExpectEntryMetric(
entry, UkmEntry::kblocked_medium_priorityName, 4);
test_ukm_recorder.ExpectEntryMetric(entry,
UkmEntry::kblocked_high_priorityName, 0);
test_ukm_recorder.ExpectEntryMetric(
entry, UkmEntry::kblocked_very_high_priorityName, 1);
}
// Test class that overrides field trial so that resource blocking hints apply
// to images as well.
class PreviewsResourceLoadingHintsTestBlockImages
: public PreviewsResourceLoadingHintsTest {
public:
PreviewsResourceLoadingHintsTestBlockImages() = default;
void SetUp() override {
std::map<std::string, std::string> feature_parameters;
feature_parameters["block_resource_type_1"] = "true";
scoped_feature_list_.InitAndEnableFeatureWithParameters(
blink::features::kPreviewsResourceLoadingHintsSpecificResourceTypes,
feature_parameters);
}
protected:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(PreviewsResourceLoadingHintsTestBlockImages,
OnePatternWithResourceSubtype) {
Vector<WTF::String> subresources_to_block;
subresources_to_block.push_back("foo.jpg");
PreviewsResourceLoadingHints* hints = PreviewsResourceLoadingHints::Create(
*dummy_page_holder_->GetFrame().DomWindow(),
ukm::UkmRecorder::GetNewSourceID(), subresources_to_block);
const struct {
KURL url;
bool allow_load_expected;
} tests[] = {
{KURL("https://www.example.com/"), true},
{KURL("https://www.example.com/foo.js"), true},
{KURL("https://www.example.com/foo.jpg"), false},
{KURL("https://www.example.com/pages/foo.jpg"), false},
{KURL("https://www.example.com/foobar.jpg"), true},
{KURL("https://www.example.com/barfoo.jpg"), false},
{KURL("http://www.example.com/foo.jpg"), false},
{KURL("http://www.example.com/foo.jpg?q=alpha"), false},
{KURL("http://www.example.com/bar.jpg?q=foo.jpg"), true},
{KURL("http://www.example.com/bar.jpg?q=foo.jpg#foo.jpg"), true},
};
for (const auto& test : tests) {
// By default, resource blocking hints do not apply to SVG documents.
EXPECT_TRUE(hints->AllowLoad(ResourceType::kSVGDocument, test.url,
ResourceLoadPriority::kHighest));
// Feature override should cause resource blocking hints to apply to images.
EXPECT_EQ(test.allow_load_expected,
hints->AllowLoad(ResourceType::kImage, test.url,
ResourceLoadPriority::kHighest));
EXPECT_EQ(test.allow_load_expected,
hints->AllowLoad(ResourceType::kScript, test.url,
ResourceLoadPriority::kHighest));
}
}
// Test class that overrides field trial so that resource blocking hints do not
// apply to CSS.
class PreviewsResourceLoadingHintsTestAllowCSS
: public PreviewsResourceLoadingHintsTestBlockImages {
public:
PreviewsResourceLoadingHintsTestAllowCSS() = default;
void SetUp() override {
std::map<std::string, std::string> feature_parameters;
feature_parameters["block_resource_type_2"] = "false";
scoped_feature_list_.InitAndEnableFeatureWithParameters(
blink::features::kPreviewsResourceLoadingHintsSpecificResourceTypes,
feature_parameters);
}
};
TEST_F(PreviewsResourceLoadingHintsTestAllowCSS,
OnePatternWithResourceSubtype) {
Vector<WTF::String> subresources_to_block;
subresources_to_block.push_back("foo.jpg");
PreviewsResourceLoadingHints* hints = PreviewsResourceLoadingHints::Create(
*dummy_page_holder_->GetFrame().DomWindow(),
ukm::UkmRecorder::GetNewSourceID(), subresources_to_block);
const struct {
KURL url;
bool allow_load_expected;
} tests[] = {
{KURL("https://www.example.com/"), true},
{KURL("https://www.example.com/foo.js"), true},
{KURL("https://www.example.com/foo.jpg"), false},
{KURL("https://www.example.com/pages/foo.jpg"), false},
{KURL("https://www.example.com/foobar.jpg"), true},
{KURL("https://www.example.com/barfoo.jpg"), false},
{KURL("http://www.example.com/foo.jpg"), false},
{KURL("http://www.example.com/foo.jpg?q=alpha"), false},
{KURL("http://www.example.com/bar.jpg?q=foo.jpg"), true},
{KURL("http://www.example.com/bar.jpg?q=foo.jpg#foo.jpg"), true},
};
for (const auto& test : tests) {
// Feature override should cause resource blocking hints to apply to only
// scripts and fonts.
EXPECT_TRUE(hints->AllowLoad(ResourceType::kImage, test.url,
ResourceLoadPriority::kHighest));
EXPECT_TRUE(hints->AllowLoad(ResourceType::kCSSStyleSheet, test.url,
ResourceLoadPriority::kHighest));
EXPECT_EQ(test.allow_load_expected,
hints->AllowLoad(ResourceType::kScript, test.url,
ResourceLoadPriority::kHighest));
EXPECT_EQ(test.allow_load_expected,
hints->AllowLoad(ResourceType::kFont, test.url,
ResourceLoadPriority::kHighest));
}
}
} // namespace
} // namespace blink