Commit 424c35a0 authored by Kunihiko Sakamoto's avatar Kunihiko Sakamoto Committed by Commit Bot

Preload scan for module scripts

Preload for <script type="module"> was disabled to prevent duplicate
loads due to credentials mode mismatch. This patch teaches the preload
scanner to set correct fetch parameters for module scripts (credentials
mode and always use UTF-8) and re-enable preload.

Change-Id: I0defc0b87681357eaccf24dd8f7a4250a0fa86f4
Reviewed-on: https://chromium-review.googlesource.com/658016
Commit-Queue: Kunihiko Sakamoto <ksakamoto@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Cr-Commit-Position: refs/heads/master@{#501532}
parent 6addf818
......@@ -238,6 +238,12 @@ class TokenPreloadScanner::StartTagScanner {
if (!request)
return nullptr;
if (Match(tag_impl_, scriptTag)) {
request->SetScriptType(type_attribute_value_ == "module"
? ScriptType::kModule
: ScriptType::kClassic);
}
request->SetCrossOrigin(cross_origin_);
request->SetNonce(nonce_);
request->SetCharset(Charset());
......@@ -520,10 +526,6 @@ class TokenPreloadScanner::StartTagScanner {
ScriptLoader::kAllowLegacyTypeInTypeAttribute, script_type)) {
return false;
}
// TODO(kouhei): Enable preload for module scripts, with correct
// credentials mode.
if (type_attribute_value_ == "module")
return false;
if (ScriptLoader::BlockForNoModule(script_type,
nomodule_attribute_value_)) {
return false;
......
......@@ -53,6 +53,13 @@ struct ReferrerPolicyTestCase {
const char* expected_referrer;
};
struct CORSTestCase {
const char* base_url;
const char* input_html;
WebURLRequest::FetchRequestMode request_mode;
WebURLRequest::FetchCredentialsMode credentials_mode;
};
struct NonceTestCase {
const char* base_url;
const char* input_html;
......@@ -132,6 +139,19 @@ class HTMLMockHTMLResourcePreloader : public ResourcePreloader {
}
}
void CORSRequestVerification(
Document* document,
WebURLRequest::FetchRequestMode request_mode,
WebURLRequest::FetchCredentialsMode credentials_mode) {
ASSERT_TRUE(preload_request_.get());
Resource* resource = preload_request_->Start(document);
ASSERT_TRUE(resource);
EXPECT_EQ(request_mode,
resource->GetResourceRequest().GetFetchRequestMode());
EXPECT_EQ(credentials_mode,
resource->GetResourceRequest().GetFetchCredentialsMode());
}
void NonceRequestVerification(const char* nonce) {
ASSERT_TRUE(preload_request_.get());
if (strlen(nonce))
......@@ -254,6 +274,18 @@ class HTMLPreloadScannerTest : public ::testing::Test {
}
}
void Test(CORSTestCase test_case) {
HTMLMockHTMLResourcePreloader 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.CORSRequestVerification(&dummy_page_holder_->GetDocument(),
test_case.request_mode,
test_case.credentials_mode);
}
void Test(NonceTestCase test_case) {
HTMLMockHTMLResourcePreloader preloader;
KURL base_url(kParsedURLString, test_case.base_url);
......@@ -760,6 +792,38 @@ TEST_F(HTMLPreloadScannerTest, testReferrerPolicy) {
Test(test_case);
}
TEST_F(HTMLPreloadScannerTest, testCORS) {
CORSTestCase test_cases[] = {
{"http://example.test", "<script src='/script'></script>",
WebURLRequest::kFetchRequestModeNoCORS,
WebURLRequest::kFetchCredentialsModeInclude},
{"http://example.test", "<script crossorigin src='/script'></script>",
WebURLRequest::kFetchRequestModeCORS,
WebURLRequest::kFetchCredentialsModeSameOrigin},
{"http://example.test",
"<script crossorigin=use-credentials src='/script'></script>",
WebURLRequest::kFetchRequestModeCORS,
WebURLRequest::kFetchCredentialsModeInclude},
{"http://example.test", "<script type='module' src='/script'></script>",
WebURLRequest::kFetchRequestModeCORS,
WebURLRequest::kFetchCredentialsModeOmit},
{"http://example.test",
"<script type='module' crossorigin='anonymous' src='/script'></script>",
WebURLRequest::kFetchRequestModeCORS,
WebURLRequest::kFetchCredentialsModeSameOrigin},
{"http://example.test",
"<script type='module' crossorigin='use-credentials' "
"src='/script'></script>",
WebURLRequest::kFetchRequestModeCORS,
WebURLRequest::kFetchCredentialsModeInclude},
};
for (const auto& test_case : test_cases) {
SCOPED_TRACE(test_case.input_html);
Test(test_case);
}
}
TEST_F(HTMLPreloadScannerTest, testNonce) {
NonceTestCase test_cases[] = {
{"http://example.test", "<script src='/script'></script>", ""},
......
......@@ -63,7 +63,24 @@ Resource* PreloadRequest::Start(Document* document) {
kCrossOriginAttributeAnonymous);
}
if (cross_origin_ != kCrossOriginAttributeNotSet) {
if (script_type_ == ScriptType::kModule) {
DCHECK_EQ(resource_type_, Resource::kScript);
WebURLRequest::FetchCredentialsMode credentials_mode =
WebURLRequest::kFetchCredentialsModeOmit;
switch (cross_origin_) {
case kCrossOriginAttributeNotSet:
credentials_mode = WebURLRequest::kFetchCredentialsModeOmit;
break;
case kCrossOriginAttributeAnonymous:
credentials_mode = WebURLRequest::kFetchCredentialsModeSameOrigin;
break;
case kCrossOriginAttributeUseCredentials:
credentials_mode = WebURLRequest::kFetchCredentialsModeInclude;
break;
}
params.SetCrossOriginAccessControl(document->GetSecurityOrigin(),
credentials_mode);
} else if (cross_origin_ != kCrossOriginAttributeNotSet) {
params.SetCrossOriginAccessControl(document->GetSecurityOrigin(),
cross_origin_);
}
......@@ -78,9 +95,13 @@ Resource* PreloadRequest::Start(Document* document) {
if (request_type_ == kRequestTypeLinkRelPreload)
params.SetLinkPreload(true);
if (resource_type_ == Resource::kScript ||
resource_type_ == Resource::kCSSStyleSheet ||
resource_type_ == Resource::kImportResource) {
if (script_type_ == ScriptType::kModule) {
DCHECK_EQ(resource_type_, Resource::kScript);
params.SetDecoderOptions(
TextResourceDecoderOptions::CreateAlwaysUseUTF8ForText());
} else if (resource_type_ == Resource::kScript ||
resource_type_ == Resource::kCSSStyleSheet ||
resource_type_ == Resource::kImportResource) {
params.SetCharset(charset_.IsEmpty() ? document->Encoding()
: WTF::TextEncoding(charset_));
}
......
......@@ -7,6 +7,7 @@
#include <memory>
#include "core/CoreExport.h"
#include "core/dom/Script.h"
#include "platform/CrossOriginAttributeValue.h"
#include "platform/loader/fetch/ClientHintsPreferences.h"
#include "platform/loader/fetch/FetchParameters.h"
......@@ -95,6 +96,8 @@ class CORE_EXPORT PreloadRequest {
}
ReferrerPolicy GetReferrerPolicy() const { return referrer_policy_; }
void SetScriptType(ScriptType script_type) { script_type_ = script_type; }
// Only scripts and css stylesheets need to have integrity set on preloads.
// This is because neither resource keeps raw data around to redo an
// integrity check. A resource in memory cache needs integrity
......@@ -127,6 +130,7 @@ class CORE_EXPORT PreloadRequest {
resource_url_(resource_url.IsolatedCopy()),
base_url_(base_url.Copy()),
resource_type_(resource_type),
script_type_(ScriptType::kClassic),
cross_origin_(kCrossOriginAttributeNotSet),
discovery_time_(MonotonicallyIncreasingTime()),
defer_(FetchParameters::kNoDefer),
......@@ -146,6 +150,7 @@ class CORE_EXPORT PreloadRequest {
KURL base_url_;
String charset_;
Resource::Type resource_type_;
ScriptType script_type_;
CrossOriginAttributeValue cross_origin_;
String nonce_;
double discovery_time_;
......
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