Commit babb7e6d authored by yoav's avatar yoav Committed by Commit bot

Avoid sending mixed-content requests for ImageSet contexts

ImageSet context resources, namely <picture> and srcset based images,
are blockable mixed-content, yet we were sending out such requests, even
if not displaying them, due to the preloadScanner ignoring the difference
between them and `<img src>` based images

This CL fixes that, by blocking such images.

BUG=713440

Review-Url: https://codereview.chromium.org/2855163002
Cr-Commit-Position: refs/heads/master@{#469314}
parent 0197d198
<!DOCTYPE html>
<html>
<head>
<title>Mixed-Content: imageset tests</title>
<meta charset='utf-8'>
<meta name="description" content="Test behavior of imageset resources, making sure that resources are not downloaded.">
<link rel="author" title="Yoav Weiss" href="yoav@yoav.ws">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/preload/resources/preload_helper.js"></script>
<script>
var t = async_test('Makes sure imageset blockable resources are not downloaded');
</script>
</head>
<body>
<img src="http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?img_src">
<picture>
<source srcset="http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?picture">
<img>
</picture>
<img srcset="http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?img_srcset">
<script>
window.addEventListener("load", t.step_func(function() {
verifyNumberOfDownloads("http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?img_src", 1);
verifyNumberOfDownloads("http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?picture", 0);
verifyNumberOfDownloads("http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?img_srcset", 0);
t.done();
}));
</script>
</body>
</html>
......@@ -241,7 +241,8 @@ void CSSPreloadScanner::EmitRule(const SegmentedString& source) {
auto request = PreloadRequest::CreateIfNeeded(
FetchInitiatorTypeNames::css, position, url,
*predicted_base_element_url_, Resource::kCSSStyleSheet,
referrer_policy_, PreloadRequest::kBaseUrlIsReferrer);
referrer_policy_, PreloadRequest::kBaseUrlIsReferrer,
ResourceFetcher::kImageNotImageSet);
if (request) {
// FIXME: Should this be including the charset in the preload request?
requests_->push_back(std::move(request));
......
......@@ -236,6 +236,11 @@ class TokenPreloadScanner::StartTagScanner {
source_size_set = picture_data.source_size_set;
source_size = picture_data.source_size;
}
ResourceFetcher::IsImageSet is_image_set =
(picture_data.picked || !srcset_image_candidate_.IsEmpty())
? ResourceFetcher::kImageIsImageSet
: ResourceFetcher::kImageNotImageSet;
if (source_size_set) {
resource_width.width = source_size;
resource_width.is_set = true;
......@@ -252,7 +257,7 @@ class TokenPreloadScanner::StartTagScanner {
auto request = PreloadRequest::CreateIfNeeded(
InitiatorFor(tag_impl_), position, url_to_load_, predicted_base_url,
type.value(), referrer_policy, PreloadRequest::kDocumentIsReferrer,
resource_width, client_hints_preferences, request_type);
is_image_set, resource_width, client_hints_preferences, request_type);
if (!request)
return nullptr;
......@@ -805,8 +810,10 @@ void TokenPreloadScanner::ScanCommon(const Token& token,
in_script_ = false;
return;
}
if (Match(tag_impl, pictureTag))
if (Match(tag_impl, pictureTag)) {
in_picture_ = false;
picture_data_.picked = false;
}
return;
}
case HTMLToken::kStartTag: {
......
......@@ -58,6 +58,13 @@ struct NonceTestCase {
const char* nonce;
};
struct ContextTestCase {
const char* base_url;
const char* input_html;
const char* preloaded_url; // Or nullptr if no preload is expected.
bool is_image_set;
};
class MockHTMLResourcePreloader : public ResourcePreloader {
public:
void PreloadRequestVerification(Resource::Type type,
......@@ -127,6 +134,11 @@ class MockHTMLResourcePreloader : public ResourcePreloader {
EXPECT_TRUE(preload_request_->Nonce().IsEmpty());
}
void ContextVerification(bool is_image_set) {
ASSERT_TRUE(preload_request_.get());
EXPECT_EQ(preload_request_->IsImageSetForTestingOnly(), is_image_set);
}
protected:
void Preload(std::unique_ptr<PreloadRequest> preload_request,
const NetworkHintsInterface&) override {
......@@ -246,6 +258,16 @@ class HTMLPreloadScannerTest : public testing::Test {
preloader.NonceRequestVerification(test_case.nonce);
}
void Test(ContextTestCase test_case) {
MockHTMLResourcePreloader preloader;
KURL base_url(kParsedURLString, test_case.base_url);
scanner_->AppendToEnd(String(test_case.input_html));
PreloadRequestStream requests = scanner_->Scan(base_url, nullptr);
preloader.TakeAndPreload(requests);
preloader.ContextVerification(test_case.is_image_set);
}
private:
std::unique_ptr<DummyPageHolder> dummy_page_holder_;
std::unique_ptr<HTMLPreloadScanner> scanner_;
......@@ -642,6 +664,19 @@ TEST_F(HTMLPreloadScannerTest, testPicture) {
Test(test_case);
}
TEST_F(HTMLPreloadScannerTest, testContext) {
ContextTestCase test_cases[] = {
{"http://example.test",
"<picture><source srcset='srcset_bla.gif'><img src='bla.gif'></picture>",
"srcset_bla.gif", true},
{"http://example.test", "<img src='bla.gif'>", "bla.gif", false},
{"http://example.test", "<img srcset='bla.gif'>", "bla.gif", true},
};
for (const auto& test_case : test_cases)
Test(test_case);
}
TEST_F(HTMLPreloadScannerTest, testReferrerPolicy) {
ReferrerPolicyTestCase test_cases[] = {
{"http://example.test", "<img src='bla.gif'/>", "bla.gif",
......
......@@ -53,8 +53,8 @@ class HTMLResourcePreloaderTest : public testing::Test {
String(), TextPosition(), test_case.url,
KURL(ParsedURLStringTag(), test_case.base_url), Resource::kImage,
ReferrerPolicy(), PreloadRequest::kDocumentIsReferrer,
FetchParameters::ResourceWidth(), ClientHintsPreferences(),
PreloadRequest::kRequestTypePreconnect);
ResourceFetcher::kImageNotImageSet, FetchParameters::ResourceWidth(),
ClientHintsPreferences(), PreloadRequest::kRequestTypePreconnect);
DCHECK(preload_request);
if (test_case.is_cors)
preload_request->SetCrossOrigin(kCrossOriginAttributeAnonymous);
......
......@@ -45,8 +45,8 @@ Resource* PreloadRequest::Start(Document* document) {
referrer_source_ == kBaseUrlIsReferrer
? base_url_.StrippedForUseAsReferrer()
: document->OutgoingReferrer()));
resource_request.SetRequestContext(
ResourceFetcher::DetermineRequestContext(resource_type_, false));
resource_request.SetRequestContext(ResourceFetcher::DetermineRequestContext(
resource_type_, is_image_set_, false));
if (resource_type_ == Resource::kScript)
MaybeDisallowFetchForDocWrittenScript(resource_request, defer_, *document);
......
......@@ -12,6 +12,7 @@
#include "platform/loader/fetch/FetchParameters.h"
#include "platform/loader/fetch/IntegrityMetadata.h"
#include "platform/loader/fetch/Resource.h"
#include "platform/loader/fetch/ResourceFetcher.h"
#include "platform/weborigin/SecurityPolicy.h"
#include "platform/wtf/Allocator.h"
#include "platform/wtf/PtrUtil.h"
......@@ -43,6 +44,7 @@ class CORE_EXPORT PreloadRequest {
Resource::Type resource_type,
const ReferrerPolicy referrer_policy,
ReferrerSource referrer_source,
ResourceFetcher::IsImageSet is_image_set,
const FetchParameters::ResourceWidth& resource_width =
FetchParameters::ResourceWidth(),
const ClientHintsPreferences& client_hints_preferences =
......@@ -59,7 +61,7 @@ class CORE_EXPORT PreloadRequest {
return WTF::WrapUnique(new PreloadRequest(
initiator_name, initiator_position, resource_url, base_url,
resource_type, resource_width, client_hints_preferences, request_type,
referrer_policy, referrer_source));
referrer_policy, referrer_source, is_image_set));
}
bool IsSafeToSendToAnotherThread() const;
......@@ -102,6 +104,10 @@ class CORE_EXPORT PreloadRequest {
from_insertion_scanner_ = from_insertion_scanner;
}
bool IsImageSetForTestingOnly() const {
return is_image_set_ == ResourceFetcher::kImageIsImageSet;
}
private:
PreloadRequest(const String& initiator_name,
const TextPosition& initiator_position,
......@@ -112,7 +118,8 @@ class CORE_EXPORT PreloadRequest {
const ClientHintsPreferences& client_hints_preferences,
RequestType request_type,
const ReferrerPolicy referrer_policy,
ReferrerSource referrer_source)
ReferrerSource referrer_source,
ResourceFetcher::IsImageSet is_image_set)
: initiator_name_(initiator_name),
initiator_position_(initiator_position),
resource_url_(resource_url.IsolatedCopy()),
......@@ -126,7 +133,8 @@ class CORE_EXPORT PreloadRequest {
request_type_(request_type),
referrer_policy_(referrer_policy),
referrer_source_(referrer_source),
from_insertion_scanner_(false) {}
from_insertion_scanner_(false),
is_image_set_(is_image_set) {}
KURL CompleteURL(Document*);
......@@ -147,6 +155,7 @@ class CORE_EXPORT PreloadRequest {
ReferrerSource referrer_source_;
IntegrityMetadataSet integrity_metadata_;
bool from_insertion_scanner_;
ResourceFetcher::IsImageSet is_image_set_;
};
typedef Vector<std::unique_ptr<PreloadRequest>> PreloadRequestStream;
......
......@@ -350,8 +350,8 @@ static Resource* PreloadIfNeeded(const LinkRelAttribute& rel_attribute,
return nullptr;
}
ResourceRequest resource_request(document.CompleteURL(href));
resource_request.SetRequestContext(
ResourceFetcher::DetermineRequestContext(resource_type.value(), false));
resource_request.SetRequestContext(ResourceFetcher::DetermineRequestContext(
resource_type.value(), ResourceFetcher::kImageNotImageSet, false));
if (referrer_policy != kReferrerPolicyDefault) {
resource_request.SetHTTPReferrer(SecurityPolicy::GenerateReferrer(
......
......@@ -15,7 +15,8 @@ Resource* LinkFetchResource::Fetch(Resource::Type type,
DCHECK_EQ(type, kLinkPrefetch);
DCHECK_EQ(params.GetResourceRequest().GetFrameType(),
WebURLRequest::kFrameTypeNone);
params.SetRequestContext(fetcher->DetermineRequestContext(type));
params.SetRequestContext(fetcher->DetermineRequestContext(
type, ResourceFetcher::kImageNotImageSet));
return fetcher->RequestResource(params, LinkResourceFactory(type));
}
......
......@@ -218,7 +218,10 @@ static void PopulateTimingInfo(ResourceTimingInfo* info, Resource* resource) {
WebURLRequest::RequestContext ResourceFetcher::DetermineRequestContext(
Resource::Type type,
IsImageSet is_image_set,
bool is_main_frame) {
DCHECK((is_image_set == kImageNotImageSet) ||
(type == Resource::kImage && is_image_set == kImageIsImageSet));
switch (type) {
case Resource::kMainResource:
if (!is_main_frame)
......@@ -235,6 +238,8 @@ WebURLRequest::RequestContext ResourceFetcher::DetermineRequestContext(
case Resource::kFont:
return WebURLRequest::kRequestContextFont;
case Resource::kImage:
if (is_image_set == kImageIsImageSet)
return WebURLRequest::kRequestContextImageSet;
return WebURLRequest::kRequestContextImage;
case Resource::kRaw:
return WebURLRequest::kRequestContextSubresource;
......@@ -706,8 +711,9 @@ void ResourceFetcher::ResourceTimingReportTimerFired(TimerBase* timer) {
}
WebURLRequest::RequestContext ResourceFetcher::DetermineRequestContext(
Resource::Type type) const {
return DetermineRequestContext(type, Context().IsMainFrame());
Resource::Type type,
IsImageSet is_image_set) const {
return DetermineRequestContext(type, is_image_set, Context().IsMainFrame());
}
void ResourceFetcher::InitializeResourceRequest(
......@@ -719,7 +725,7 @@ void ResourceFetcher::InitializeResourceRequest(
Context().ResourceRequestCachePolicy(request, type, defer));
}
if (request.GetRequestContext() == WebURLRequest::kRequestContextUnspecified)
request.SetRequestContext(DetermineRequestContext(type));
request.SetRequestContext(DetermineRequestContext(type, kImageNotImageSet));
if (type == Resource::kLinkPrefetch)
request.SetHTTPHeaderField(HTTPNames::Purpose, "prefetch");
......
......@@ -130,10 +130,13 @@ class PLATFORM_EXPORT ResourceFetcher
String GetCacheIdentifier() const;
enum IsImageSet { kImageNotImageSet, kImageIsImageSet };
WARN_UNUSED_RESULT static WebURLRequest::RequestContext
DetermineRequestContext(Resource::Type, bool is_main_frame);
DetermineRequestContext(Resource::Type, IsImageSet, bool is_main_frame);
WARN_UNUSED_RESULT WebURLRequest::RequestContext DetermineRequestContext(
Resource::Type) const;
Resource::Type,
IsImageSet) const;
void UpdateAllImageResourcePriorities();
......
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