Don't preload scripts with invalid type/language attributes
This patch keeps track of a script tag's type and language attributes,
and gates preloading if they are invalid. This aligns the preload scanner's
policy with the ScriptLoader's policy.
BUG=623109
Review-Url: https://codereview.chromium.org/2099853002
Cr-Commit-Position: refs/heads/master@{#403182}
diff --git a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
index a8337a8d..6ae5e2c 100644
--- a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
+++ b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp
@@ -121,7 +121,7 @@
m_pendingScript = nullptr;
}
-// Helper function
+// Helper function. Must take a lowercase language as input.
static bool isLegacySupportedJavaScriptLanguage(const String& language)
{
// Mozilla 1.8 accepts javascript1.0 - javascript1.7, but WinIE 7 accepts only javascript1.1 - javascript1.3.
@@ -131,24 +131,19 @@
// We want to accept all the values that either of these browsers accept, but not other values.
// FIXME: This function is not HTML5 compliant. These belong in the MIME registry as "text/javascript<version>" entries.
- typedef HashSet<String, CaseFoldingHash> LanguageSet;
- DEFINE_STATIC_LOCAL(LanguageSet, languages, ());
- if (languages.isEmpty()) {
- languages.add("javascript");
- languages.add("javascript1.0");
- languages.add("javascript1.1");
- languages.add("javascript1.2");
- languages.add("javascript1.3");
- languages.add("javascript1.4");
- languages.add("javascript1.5");
- languages.add("javascript1.6");
- languages.add("javascript1.7");
- languages.add("livescript");
- languages.add("ecmascript");
- languages.add("jscript");
- }
-
- return languages.contains(language);
+ DCHECK_EQ(language, language.lower());
+ return language == "javascript"
+ || language == "javascript1.0"
+ || language == "javascript1.1"
+ || language == "javascript1.2"
+ || language == "javascript1.3"
+ || language == "javascript1.4"
+ || language == "javascript1.5"
+ || language == "javascript1.6"
+ || language == "javascript1.7"
+ || language == "livescript"
+ || language == "ecmascript"
+ || language == "jscript";
}
void ScriptLoader::dispatchErrorEvent()
@@ -163,29 +158,30 @@
setHaveFiredLoadEvent(true);
}
-bool ScriptLoader::isScriptTypeSupported(LegacyTypeSupport supportLegacyTypes) const
+bool ScriptLoader::isValidScriptTypeAndLanguage(const String& type, const String& language, LegacyTypeSupport supportLegacyTypes)
{
// FIXME: isLegacySupportedJavaScriptLanguage() is not valid HTML5. It is used here to maintain backwards compatibility with existing layout tests. The specific violations are:
// - Allowing type=javascript. type= should only support MIME types, such as text/javascript.
// - Allowing a different set of languages for language= and type=. language= supports Javascript 1.1 and 1.4-1.6, but type= does not.
-
- String type = client()->typeAttributeValue();
- String language = client()->languageAttributeValue();
- if (type.isEmpty() && language.isEmpty())
- return true; // Assume text/javascript.
if (type.isEmpty()) {
- type = "text/" + language.lower();
- if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type) || isLegacySupportedJavaScriptLanguage(language))
- return true;
+ String lowerLanguage = language.lower();
+ return language.isEmpty() // assume text/javascript.
+ || MIMETypeRegistry::isSupportedJavaScriptMIMEType("text/" + lowerLanguage)
+ || isLegacySupportedJavaScriptLanguage(lowerLanguage);
} else if (RuntimeEnabledFeatures::moduleScriptsEnabled() && type == "module") {
return true;
- } else if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace()) || (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLegacySupportedJavaScriptLanguage(type))) {
+ } else if (MIMETypeRegistry::isSupportedJavaScriptMIMEType(type.stripWhiteSpace()) || (supportLegacyTypes == AllowLegacyTypeInTypeAttribute && isLegacySupportedJavaScriptLanguage(type.lower()))) {
return true;
}
return false;
}
+bool ScriptLoader::isScriptTypeSupported(LegacyTypeSupport supportLegacyTypes) const
+{
+ return isValidScriptTypeAndLanguage(client()->typeAttributeValue(), client()->languageAttributeValue(), supportLegacyTypes);
+}
+
// http://dev.w3.org/html5/spec/Overview.html#prepare-a-script
bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition, LegacyTypeSupport supportLegacyTypes)
{
@@ -344,9 +340,10 @@
void ScriptLoader::logScriptMimetype(ScriptResource* resource, LocalFrame* frame, String mimetype)
{
- bool text = mimetype.lower().startsWith("text/");
- bool application = mimetype.lower().startsWith("application/");
- bool expectedJs = MIMETypeRegistry::isSupportedJavaScriptMIMEType(mimetype) || (text && isLegacySupportedJavaScriptLanguage(mimetype.substring(5)));
+ String lowerMimetype = mimetype.lower();
+ bool text = lowerMimetype.startsWith("text/");
+ bool application = lowerMimetype.startsWith("application/");
+ bool expectedJs = MIMETypeRegistry::isSupportedJavaScriptMIMEType(lowerMimetype) || (text && isLegacySupportedJavaScriptLanguage(lowerMimetype.substring(5)));
bool sameOrigin = m_element->document().getSecurityOrigin()->canRequest(m_resource->url());
if (expectedJs) {
return;
diff --git a/third_party/WebKit/Source/core/dom/ScriptLoader.h b/third_party/WebKit/Source/core/dom/ScriptLoader.h
index 0af9f42..1fc84c7d 100644
--- a/third_party/WebKit/Source/core/dom/ScriptLoader.h
+++ b/third_party/WebKit/Source/core/dom/ScriptLoader.h
@@ -50,6 +50,8 @@
Element* element() const { return m_element; }
enum LegacyTypeSupport { DisallowLegacyTypeInTypeAttribute, AllowLegacyTypeInTypeAttribute };
+ static bool isValidScriptTypeAndLanguage(const String& typeAttributeValue, const String& languageAttributeValue, LegacyTypeSupport supportLegacyTypes);
+
bool prepareScript(const TextPosition& scriptStartPosition = TextPosition::minimumPosition(), LegacyTypeSupport = DisallowLegacyTypeInTypeAttribute);
String scriptCharset() const { return m_characterEncoding; }
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLPreloadScanner.cpp b/third_party/WebKit/Source/core/html/parser/HTMLPreloadScanner.cpp
index 14ecba3..a6b5992 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLPreloadScanner.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLPreloadScanner.cpp
@@ -34,6 +34,7 @@
#include "core/css/MediaValuesCached.h"
#include "core/css/parser/SizesAttributeParser.h"
#include "core/dom/Document.h"
+#include "core/dom/ScriptLoader.h"
#include "core/fetch/IntegrityMetadata.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h"
@@ -221,7 +222,6 @@
}
}
-
TextPosition position = TextPosition(source.currentLine(), source.currentColumn());
FetchRequest::ResourceWidth resourceWidth;
float sourceSize = m_sourceSize;
@@ -272,6 +272,10 @@
// explanation.
else if (match(attributeName, integrityAttr))
SubresourceIntegrity::parseIntegrityAttribute(attributeValue, m_integrityMetadata);
+ else if (match(attributeName, typeAttr))
+ m_typeAttributeValue = attributeValue;
+ else if (match(attributeName, languageAttr))
+ m_languageAttributeValue = attributeValue;
}
template<typename NameType>
@@ -440,6 +444,8 @@
return false;
if (match(m_tagImpl, inputTag) && !m_inputIsImage)
return false;
+ if (match(m_tagImpl, scriptTag) && !ScriptLoader::isValidScriptTypeAndLanguage(m_typeAttributeValue, m_languageAttributeValue, ScriptLoader::AllowLegacyTypeInTypeAttribute))
+ return false;
return true;
}
@@ -471,6 +477,8 @@
String m_imgSrcUrl;
String m_srcsetAttributeValue;
String m_asAttributeValue;
+ String m_typeAttributeValue;
+ String m_languageAttributeValue;
float m_sourceSize;
bool m_sourceSizeSet;
FetchRequest::DeferOption m_defer;
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp b/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp
index bc584d1e..8932e764 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLPreloadScannerTest.cpp
@@ -414,4 +414,33 @@
test(testCase);
}
+// The preload scanner should follow the same policy that the ScriptLoader does
+// with regard to the type and language attribute.
+TEST_F(HTMLPreloadScannerTest, testScriptTypeAndLanguage)
+{
+ TestCase testCases[] = {
+ // Allow empty src and language attributes.
+ {"http://example.test", "<script src='test.js'></script>", "test.js", "http://example.test/", Resource::Script, 0},
+ {"http://example.test", "<script type='' language='' src='test.js'></script>", "test.js", "http://example.test/", Resource::Script, 0},
+ // Allow standard language and type attributes.
+ {"http://example.test", "<script type='text/javascript' src='test.js'></script>", "test.js", "http://example.test/", Resource::Script, 0},
+ {"http://example.test", "<script type='text/javascript' language='javascript' src='test.js'></script>", "test.js", "http://example.test/", Resource::Script, 0},
+ // Allow legacy languages in the "language" attribute with an empty
+ // type.
+ {"http://example.test", "<script language='javascript1.1' src='test.js'></script>", "test.js", "http://example.test/", Resource::Script, 0},
+ // Allow legacy languages in the "type" attribute.
+ {"http://example.test", "<script type='javascript' src='test.js'></script>", "test.js", "http://example.test/", Resource::Script, 0},
+ {"http://example.test", "<script type='javascript1.7' src='test.js'></script>", "test.js", "http://example.test/", Resource::Script, 0},
+ // Do not allow invalid types in the "type" attribute.
+ {"http://example.test", "<script type='invalid' src='test.js'></script>", nullptr, "http://example.test/", Resource::Script, 0},
+ {"http://example.test", "<script type='asdf' src='test.js'></script>", nullptr, "http://example.test/", Resource::Script, 0},
+ // Do not allow invalid languages.
+ {"http://example.test", "<script language='french' src='test.js'></script>", nullptr, "http://example.test/", Resource::Script, 0},
+ {"http://example.test", "<script language='python' src='test.js'></script>", nullptr, "http://example.test/", Resource::Script, 0},
+ };
+
+ for (const auto& testCase : testCases)
+ test(testCase);
+}
+
} // namespace blink