Commit 5b86dfe6 authored by yoav's avatar yoav Committed by Commit bot

Fix HTMLPreloadScanner handling of type in link preload.

Handling of the `type` attribute in `<link rel=preload>` was not
properly added, which meant preloads that included a valid type were
dismissed due to rel=stylesheet related logic.

This CL adds proper handling for the `type` attribute in
`<link rel=preload>` for fonts, images and styles.

BUG=659640

Review-Url: https://codereview.chromium.org/2620993002
Cr-Commit-Position: refs/heads/master@{#442887}
parent d3e6557e
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include "platform/instrumentation/tracing/TraceEvent.h" #include "platform/instrumentation/tracing/TraceEvent.h"
#include "platform/network/mime/ContentType.h" #include "platform/network/mime/ContentType.h"
#include "platform/network/mime/MIMETypeRegistry.h" #include "platform/network/mime/MIMETypeRegistry.h"
#include "wtf/Optional.h"
#include <memory> #include <memory>
namespace blink { namespace blink {
...@@ -120,7 +121,7 @@ static String initiatorFor(const StringImpl* tagImpl) { ...@@ -120,7 +121,7 @@ static String initiatorFor(const StringImpl* tagImpl) {
return scriptTag.localName(); return scriptTag.localName();
if (match(tagImpl, videoTag)) if (match(tagImpl, videoTag))
return videoTag.localName(); return videoTag.localName();
ASSERT_NOT_REACHED(); NOTREACHED();
return emptyString(); return emptyString();
} }
...@@ -205,13 +206,17 @@ class TokenPreloadScanner::StartTagScanner { ...@@ -205,13 +206,17 @@ class TokenPreloadScanner::StartTagScanner {
const ReferrerPolicy documentReferrerPolicy) { const ReferrerPolicy documentReferrerPolicy) {
PreloadRequest::RequestType requestType = PreloadRequest::RequestType requestType =
PreloadRequest::RequestTypePreload; PreloadRequest::RequestTypePreload;
WTF::Optional<Resource::Type> type;
if (shouldPreconnect()) { if (shouldPreconnect()) {
requestType = PreloadRequest::RequestTypePreconnect; requestType = PreloadRequest::RequestTypePreconnect;
} else { } else {
if (isLinkRelPreload()) { if (isLinkRelPreload()) {
requestType = PreloadRequest::RequestTypeLinkRelPreload; requestType = PreloadRequest::RequestTypeLinkRelPreload;
type = resourceTypeForLinkPreload();
if (type == WTF::nullopt)
return nullptr;
} }
if (!shouldPreload()) { if (!shouldPreload(type)) {
return nullptr; return nullptr;
} }
} }
...@@ -230,9 +235,8 @@ class TokenPreloadScanner::StartTagScanner { ...@@ -230,9 +235,8 @@ class TokenPreloadScanner::StartTagScanner {
resourceWidth.isSet = true; resourceWidth.isSet = true;
} }
Resource::Type type; if (type == WTF::nullopt)
if (!resourceType(type)) type = resourceType();
return nullptr;
// The element's 'referrerpolicy' attribute (if present) takes precedence // The element's 'referrerpolicy' attribute (if present) takes precedence
// over the document's referrer policy. // over the document's referrer policy.
...@@ -240,8 +244,9 @@ class TokenPreloadScanner::StartTagScanner { ...@@ -240,8 +244,9 @@ class TokenPreloadScanner::StartTagScanner {
? m_referrerPolicy ? m_referrerPolicy
: documentReferrerPolicy; : documentReferrerPolicy;
auto request = PreloadRequest::createIfNeeded( auto request = PreloadRequest::createIfNeeded(
initiatorFor(m_tagImpl), position, m_urlToLoad, predictedBaseURL, type, initiatorFor(m_tagImpl), position, m_urlToLoad, predictedBaseURL,
referrerPolicy, resourceWidth, clientHintsPreferences, requestType); type.value(), referrerPolicy, resourceWidth, clientHintsPreferences,
requestType);
if (!request) if (!request)
return nullptr; return nullptr;
...@@ -349,8 +354,7 @@ class TokenPreloadScanner::StartTagScanner { ...@@ -349,8 +354,7 @@ class TokenPreloadScanner::StartTagScanner {
} else if (match(attributeName, asAttr)) { } else if (match(attributeName, asAttr)) {
m_asAttributeValue = attributeValue.lower(); m_asAttributeValue = attributeValue.lower();
} else if (match(attributeName, typeAttr)) { } else if (match(attributeName, typeAttr)) {
m_matched &= MIMETypeRegistry::isSupportedStyleSheetMIMEType( m_typeAttributeValue = attributeValue;
ContentType(attributeValue).type());
} else if (!m_referrerPolicySet && } else if (!m_referrerPolicySet &&
match(attributeName, referrerpolicyAttr) && match(attributeName, referrerpolicyAttr) &&
!attributeValue.isNull()) { !attributeValue.isNull()) {
...@@ -443,25 +447,26 @@ class TokenPreloadScanner::StartTagScanner { ...@@ -443,25 +447,26 @@ class TokenPreloadScanner::StartTagScanner {
return m_charset; return m_charset;
} }
bool resourceType(Resource::Type& type) const { WTF::Optional<Resource::Type> resourceTypeForLinkPreload() const {
DCHECK(m_linkIsPreload);
return LinkLoader::getResourceTypeFromAsAttribute(m_asAttributeValue);
}
Resource::Type resourceType() const {
if (match(m_tagImpl, scriptTag)) { if (match(m_tagImpl, scriptTag)) {
type = Resource::Script; return Resource::Script;
} else if (match(m_tagImpl, imgTag) || match(m_tagImpl, videoTag) || } else if (match(m_tagImpl, imgTag) || match(m_tagImpl, videoTag) ||
(match(m_tagImpl, inputTag) && m_inputIsImage)) { (match(m_tagImpl, inputTag) && m_inputIsImage)) {
type = Resource::Image; return Resource::Image;
} else if (match(m_tagImpl, linkTag) && m_linkIsStyleSheet) { } else if (match(m_tagImpl, linkTag) && m_linkIsStyleSheet) {
type = Resource::CSSStyleSheet; return Resource::CSSStyleSheet;
} else if (m_linkIsPreconnect) { } else if (m_linkIsPreconnect) {
type = Resource::Raw; return Resource::Raw;
} else if (m_linkIsPreload) {
if (!LinkLoader::getResourceTypeFromAsAttribute(m_asAttributeValue, type))
return false;
} else if (match(m_tagImpl, linkTag) && m_linkIsImport) { } else if (match(m_tagImpl, linkTag) && m_linkIsImport) {
type = Resource::ImportResource; return Resource::ImportResource;
} else {
ASSERT_NOT_REACHED();
} }
return true; NOTREACHED();
return Resource::Raw;
} }
bool shouldPreconnect() const { bool shouldPreconnect() const {
...@@ -474,14 +479,39 @@ class TokenPreloadScanner::StartTagScanner { ...@@ -474,14 +479,39 @@ class TokenPreloadScanner::StartTagScanner {
!m_urlToLoad.isEmpty(); !m_urlToLoad.isEmpty();
} }
bool shouldPreload() const { bool shouldPreloadLink(WTF::Optional<Resource::Type>& type) const {
if (m_linkIsStyleSheet) {
return m_typeAttributeValue.isEmpty() ||
MIMETypeRegistry::isSupportedStyleSheetMIMEType(
ContentType(m_typeAttributeValue).type());
} else if (m_linkIsPreload) {
if (m_typeAttributeValue.isEmpty())
return true;
String typeFromAttribute = ContentType(m_typeAttributeValue).type();
if ((type == Resource::Font &&
!MIMETypeRegistry::isSupportedFontMIMEType(typeFromAttribute)) ||
(type == Resource::Image &&
!MIMETypeRegistry::isSupportedImagePrefixedMIMEType(
typeFromAttribute)) ||
(type == Resource::CSSStyleSheet &&
!MIMETypeRegistry::isSupportedStyleSheetMIMEType(
typeFromAttribute))) {
return false;
}
} else if (!m_linkIsImport) {
return false;
}
return true;
}
bool shouldPreload(WTF::Optional<Resource::Type>& type) const {
if (m_urlToLoad.isEmpty()) if (m_urlToLoad.isEmpty())
return false; return false;
if (!m_matched) if (!m_matched)
return false; return false;
if (match(m_tagImpl, linkTag) && !m_linkIsStyleSheet && !m_linkIsImport && if (match(m_tagImpl, linkTag))
!m_linkIsPreload) return shouldPreloadLink(type);
return false;
if (match(m_tagImpl, inputTag) && !m_inputIsImage) if (match(m_tagImpl, inputTag) && !m_inputIsImage)
return false; return false;
if (match(m_tagImpl, scriptTag) && if (match(m_tagImpl, scriptTag) &&
......
...@@ -723,12 +723,33 @@ TEST_F(HTMLPreloadScannerTest, testLinkRelPreload) { ...@@ -723,12 +723,33 @@ TEST_F(HTMLPreloadScannerTest, testLinkRelPreload) {
"http://example.test/", Resource::Raw, 0}, "http://example.test/", Resource::Raw, 0},
{"http://example.test", "<link rel=preload href=bla as=script>", "bla", {"http://example.test", "<link rel=preload href=bla as=script>", "bla",
"http://example.test/", Resource::Script, 0}, "http://example.test/", Resource::Script, 0},
{"http://example.test",
"<link rel=preload href=bla as=script type='script/foo'>", "bla",
"http://example.test/", Resource::Script, 0},
{"http://example.test", "<link rel=preload href=bla as=style>", "bla", {"http://example.test", "<link rel=preload href=bla as=style>", "bla",
"http://example.test/", Resource::CSSStyleSheet, 0}, "http://example.test/", Resource::CSSStyleSheet, 0},
{"http://example.test",
"<link rel=preload href=bla as=style type='text/css'>", "bla",
"http://example.test/", Resource::CSSStyleSheet, 0},
{"http://example.test",
"<link rel=preload href=bla as=style type='text/bla'>", nullptr,
"http://example.test/", Resource::CSSStyleSheet, 0},
{"http://example.test", "<link rel=preload href=bla as=image>", "bla", {"http://example.test", "<link rel=preload href=bla as=image>", "bla",
"http://example.test/", Resource::Image, 0}, "http://example.test/", Resource::Image, 0},
{"http://example.test",
"<link rel=preload href=bla as=image type='image/webp'>", "bla",
"http://example.test/", Resource::Image, 0},
{"http://example.test",
"<link rel=preload href=bla as=image type='image/bla'>", nullptr,
"http://example.test/", Resource::Image, 0},
{"http://example.test", "<link rel=preload href=bla as=font>", "bla", {"http://example.test", "<link rel=preload href=bla as=font>", "bla",
"http://example.test/", Resource::Font, 0}, "http://example.test/", Resource::Font, 0},
{"http://example.test",
"<link rel=preload href=bla as=font type='font/woff2'>", "bla",
"http://example.test/", Resource::Font, 0},
{"http://example.test",
"<link rel=preload href=bla as=font type='font/bla'>", nullptr,
"http://example.test/", Resource::Font, 0},
{"http://example.test", "<link rel=preload href=bla as=media>", "bla", {"http://example.test", "<link rel=preload href=bla as=media>", "bla",
"http://example.test/", Resource::Media, 0}, "http://example.test/", Resource::Media, 0},
{"http://example.test", "<link rel=preload href=bla as=track>", "bla", {"http://example.test", "<link rel=preload href=bla as=track>", "bla",
......
...@@ -182,27 +182,25 @@ static void preconnectIfNeeded( ...@@ -182,27 +182,25 @@ static void preconnectIfNeeded(
} }
} }
bool LinkLoader::getResourceTypeFromAsAttribute(const String& as, WTF::Optional<Resource::Type> LinkLoader::getResourceTypeFromAsAttribute(
Resource::Type& type) { const String& as) {
DCHECK_EQ(as.lower(), as); DCHECK_EQ(as.lower(), as);
if (as == "image") { if (as == "image") {
type = Resource::Image; return Resource::Image;
} else if (as == "script") { } else if (as == "script") {
type = Resource::Script; return Resource::Script;
} else if (as == "style") { } else if (as == "style") {
type = Resource::CSSStyleSheet; return Resource::CSSStyleSheet;
} else if (as == "media") { } else if (as == "media") {
type = Resource::Media; return Resource::Media;
} else if (as == "font") { } else if (as == "font") {
type = Resource::Font; return Resource::Font;
} else if (as == "track") { } else if (as == "track") {
type = Resource::TextTrack; return Resource::TextTrack;
} else { } else if (as.isEmpty()) {
type = Resource::Raw; return Resource::Raw;
if (!as.isEmpty())
return false;
} }
return true; return WTF::nullopt;
} }
void LinkLoader::createLinkPreloadResourceClient(Resource* resource) { void LinkLoader::createLinkPreloadResourceClient(Resource* resource) {
...@@ -300,8 +298,9 @@ static Resource* preloadIfNeeded(const LinkRelAttribute& relAttribute, ...@@ -300,8 +298,9 @@ static Resource* preloadIfNeeded(const LinkRelAttribute& relAttribute,
} }
if (caller == LinkCalledFromHeader) if (caller == LinkCalledFromHeader)
UseCounter::count(document, UseCounter::LinkHeaderPreload); UseCounter::count(document, UseCounter::LinkHeaderPreload);
Resource::Type resourceType; Optional<Resource::Type> resourceType =
if (!LinkLoader::getResourceTypeFromAsAttribute(as, resourceType)) { LinkLoader::getResourceTypeFromAsAttribute(as);
if (resourceType == WTF::nullopt) {
document.addConsoleMessage(ConsoleMessage::create( document.addConsoleMessage(ConsoleMessage::create(
OtherMessageSource, WarningMessageLevel, OtherMessageSource, WarningMessageLevel,
String("<link rel=preload> must have a valid `as` value"))); String("<link rel=preload> must have a valid `as` value")));
...@@ -309,15 +308,15 @@ static Resource* preloadIfNeeded(const LinkRelAttribute& relAttribute, ...@@ -309,15 +308,15 @@ static Resource* preloadIfNeeded(const LinkRelAttribute& relAttribute,
return nullptr; return nullptr;
} }
if (!isSupportedType(resourceType, mimeType)) { if (!isSupportedType(resourceType.value(), mimeType)) {
document.addConsoleMessage(ConsoleMessage::create( document.addConsoleMessage(ConsoleMessage::create(
OtherMessageSource, WarningMessageLevel, OtherMessageSource, WarningMessageLevel,
String("<link rel=preload> has an unsupported `type` value"))); String("<link rel=preload> has an unsupported `type` value")));
return nullptr; return nullptr;
} }
ResourceRequest resourceRequest(document.completeURL(href)); ResourceRequest resourceRequest(document.completeURL(href));
ResourceFetcher::determineRequestContext(resourceRequest, resourceType, ResourceFetcher::determineRequestContext(resourceRequest,
false); resourceType.value(), false);
if (referrerPolicy != ReferrerPolicyDefault) { if (referrerPolicy != ReferrerPolicyDefault) {
resourceRequest.setHTTPReferrer(SecurityPolicy::generateReferrer( resourceRequest.setHTTPReferrer(SecurityPolicy::generateReferrer(
...@@ -339,7 +338,7 @@ static Resource* preloadIfNeeded(const LinkRelAttribute& relAttribute, ...@@ -339,7 +338,7 @@ static Resource* preloadIfNeeded(const LinkRelAttribute& relAttribute,
} }
linkRequest.setForPreload(true, monotonicallyIncreasingTime()); linkRequest.setForPreload(true, monotonicallyIncreasingTime());
linkRequest.setLinkPreload(true); linkRequest.setLinkPreload(true);
return document.loader()->startPreload(resourceType, linkRequest); return document.loader()->startPreload(resourceType.value(), linkRequest);
} }
static Resource* prefetchIfNeeded(Document& document, static Resource* prefetchIfNeeded(Document& document,
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "platform/PrerenderClient.h" #include "platform/PrerenderClient.h"
#include "platform/Timer.h" #include "platform/Timer.h"
#include "platform/heap/Handle.h" #include "platform/heap/Handle.h"
#include "wtf/Optional.h"
namespace blink { namespace blink {
...@@ -101,7 +102,8 @@ class CORE_EXPORT LinkLoader final ...@@ -101,7 +102,8 @@ class CORE_EXPORT LinkLoader final
CanLoadResources, CanLoadResources,
MediaPreloadPolicy, MediaPreloadPolicy,
ViewportDescriptionWrapper*); ViewportDescriptionWrapper*);
static bool getResourceTypeFromAsAttribute(const String& as, Resource::Type&); static WTF::Optional<Resource::Type> getResourceTypeFromAsAttribute(
const String& as);
DECLARE_TRACE(); DECLARE_TRACE();
......
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