Index: third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp |
diff --git a/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp b/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp |
index 4ca1989f0786653063dd4dd5958df3a8e9fb5196..4cf233dd5305d4f379a6202ed52fb59ddbff9cca 100644 |
--- a/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp |
+++ b/third_party/WebKit/Source/platform/loader/fetch/ResourceFetcher.cpp |
@@ -493,11 +493,26 @@ void ResourceFetcher::UpdateMemoryCacheStats(Resource* resource, |
// dead if MemoryCache holds weak references to Resource). Currently we check |
// references to Resource from ResourceClient and |m_preloads| only, because |
// they are major sources of references. |
- if (resource && !resource->IsAlive() && !preloads_.Contains(resource)) { |
+ if (resource && !resource->IsAlive() && !ContainsAsPreload(resource)) { |
DEFINE_RESOURCE_HISTOGRAM("Dead."); |
} |
} |
+bool ResourceFetcher::ContainsAsPreload(Resource* resource) const { |
+ auto it = preloads_.find(PreloadKey(resource->Url(), resource->GetType())); |
+ return it != preloads_.end() && it->value == resource; |
+} |
+ |
+void ResourceFetcher::RemovePreload(Resource* resource) { |
+ auto it = preloads_.find(PreloadKey(resource->Url(), resource->GetType())); |
+ if (it == preloads_.end()) |
+ return; |
+ if (it->value == resource) { |
+ resource->DecreasePreloadCount(); |
+ preloads_.erase(it); |
+ } |
+} |
+ |
ResourceFetcher::PrepareRequestResult ResourceFetcher::PrepareRequest( |
FetchParameters& params, |
const ResourceFactory& factory, |
@@ -604,19 +619,30 @@ Resource* ResourceFetcher::RequestResource( |
if (!resource && !is_data_url && archive_) |
return nullptr; |
} |
- if (!resource && IsMainThread()) { |
- resource = |
- GetMemoryCache()->ResourceForURL(params.Url(), GetCacheIdentifier()); |
+ RevalidationPolicy policy = kLoad; |
+ bool preload_found = false; |
+ if (!resource) { |
+ resource = MatchPreload(params, factory.GetType()); |
+ if (resource) { |
+ preload_found = true; |
+ policy = kUse; |
+ // If |param| is for a blocking resource and a preloaded resource is |
+ // found, we may need to make it block the onload event. |
+ MakePreloadedResourceBlockOnloadIfNeeded(resource, params); |
+ } |
} |
+ if (!preload_found) { |
+ if (!resource && IsMainThread()) { |
+ resource = |
+ GetMemoryCache()->ResourceForURL(params.Url(), GetCacheIdentifier()); |
+ } |
- // If we got a preloaded resource from the cache for a non-preload request, |
- // we may need to make it block the onload event. |
- MakePreloadedResourceBlockOnloadIfNeeded(resource, params); |
- |
- const RevalidationPolicy policy = DetermineRevalidationPolicy( |
- factory.GetType(), params, resource, is_static_data); |
- TRACE_EVENT_INSTANT1("blink", "ResourceFetcher::determineRevalidationPolicy", |
- TRACE_EVENT_SCOPE_THREAD, "revalidationPolicy", policy); |
+ policy = DetermineRevalidationPolicy(factory.GetType(), params, resource, |
+ is_static_data); |
+ TRACE_EVENT_INSTANT1( |
+ "blink", "ResourceFetcher::determineRevalidationPolicy", |
+ TRACE_EVENT_SCOPE_THREAD, "revalidationPolicy", policy); |
+ } |
UpdateMemoryCacheStats(resource, policy, params, factory, is_static_data); |
@@ -669,11 +695,17 @@ Resource* ResourceFetcher::RequestResource( |
// loading immediately. If revalidation policy was determined as |Revalidate|, |
// the resource was already initialized for the revalidation here, but won't |
// start loading. |
- if (!ResourceNeedsLoad(resource, params, policy)) |
+ if (!ResourceNeedsLoad(resource, params, policy)) { |
+ if (policy != kUse) |
+ InsertAsPreloadIfNecessary(resource, params, factory.GetType()); |
return resource; |
+ } |
if (!StartLoad(resource)) |
return nullptr; |
+ |
+ if (policy != kUse) |
+ InsertAsPreloadIfNecessary(resource, params, factory.GetType()); |
scoped_resource_load_tracker.ResourceLoadContinuesBeyondScope(); |
DCHECK(!resource->ErrorOccurred() || |
@@ -828,61 +860,57 @@ void ResourceFetcher::RecordResourceTimingOnRedirect( |
} |
} |
-ResourceFetcher::RevalidationPolicy |
-ResourceFetcher::DetermineRevalidationPolicy( |
- Resource::Type type, |
- const FetchParameters& fetch_params, |
- Resource* existing_resource, |
- bool is_static_data) const { |
- const ResourceRequest& request = fetch_params.GetResourceRequest(); |
- |
- if (!existing_resource) |
- return kLoad; |
+Resource* ResourceFetcher::MatchPreload(const FetchParameters& params, |
+ Resource::Type type) { |
+ auto it = preloads_.find(PreloadKey(params.Url(), type)); |
+ if (it == preloads_.end()) |
+ return nullptr; |
- // If the existing resource is loading and the associated fetcher is not equal |
- // to |this|, we must not use the resource. Otherwise, CSP violation may |
- // happen in redirect handling. |
- if (existing_resource->Loader() && |
- existing_resource->Loader()->Fetcher() != this) { |
- return kReload; |
- } |
+ Resource* resource = it->value; |
- // Checks if the resource has an explicit policy about integrity metadata. |
- // |
- // This is necessary because ScriptResource and CSSStyleSheetResource objects |
- // do not keep the raw data around after the source is accessed once, so if |
- // the resource is accessed from the MemoryCache for a second time, there is |
- // no way to redo an integrity check. |
- // |
- // Thus, Blink implements a scheme where it caches the integrity information |
- // for those resources after the first time it is checked, and if there is |
- // another request for that resource, with the same integrity metadata, Blink |
- // skips the integrity calculation. However, if the integrity metadata is a |
- // mismatch, the MemoryCache must be skipped here, and a new request for the |
- // resource must be made to get the raw data. This is expected to be an |
- // uncommon case, however, as it implies two same-origin requests to the same |
- // resource, but with different integrity metadata. |
RecordSriResourceIntegrityMismatchEvent(kCheckingForIntegrityMismatch); |
- if (existing_resource->MustRefetchDueToIntegrityMetadata(fetch_params)) { |
- RecordSriResourceIntegrityMismatchEvent(kRefetchDueToIntegrityMismatch); |
- return kReload; |
- } |
+ if (resource->MustRefetchDueToIntegrityMetadata(params)) |
+ return nullptr; |
- // If the same URL has been loaded as a different type, we need to reload. |
- if (existing_resource->GetType() != type) { |
- // FIXME: If existingResource is a Preload and the new type is LinkPrefetch |
- // We really should discard the new prefetch since the preload has more |
- // specific type information! crbug.com/379893 |
- // fast/dom/HTMLLinkElement/link-and-subresource-test hits this case. |
- RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy " |
- "reloading due to type mismatch."; |
- return kReload; |
+ if (params.IsSpeculativePreload()) |
+ return resource; |
+ if (params.IsLinkPreload()) { |
+ resource->SetLinkPreload(true); |
+ return resource; |
} |
- // We already have a preload going for this URL. |
- if (fetch_params.IsSpeculativePreload() && existing_resource->IsPreloaded()) |
- return kUse; |
+ if (!IsReusableAlsoForPreloading(params, resource, false)) |
+ return nullptr; |
+ |
+ resource->DecreasePreloadCount(); |
+ preloads_.erase(it); |
+ matched_preloads_.push_back(resource); |
+ return resource; |
+} |
+void ResourceFetcher::InsertAsPreloadIfNecessary(Resource* resource, |
+ const FetchParameters& params, |
+ Resource::Type type) { |
+ if (!params.IsSpeculativePreload() && !params.IsLinkPreload()) |
+ return; |
+ // CSP layout tests verify that preloads are subject to access checks by |
+ // seeing if they are in the `preload started` list. Therefore do not add |
+ // them to the list if the load is immediately denied. |
+ if (resource->GetResourceError().IsAccessCheck()) |
+ return; |
+ PreloadKey key(params.Url(), type); |
+ if (preloads_.find(key) == preloads_.end()) { |
+ preloads_.insert(key, resource); |
+ resource->IncreasePreloadCount(); |
+ if (preloaded_urls_for_test_) |
+ preloaded_urls_for_test_->insert(resource->Url().GetString()); |
+ } |
+} |
+ |
+bool ResourceFetcher::IsReusableAlsoForPreloading(const FetchParameters& params, |
+ Resource* existing_resource, |
+ bool is_static_data) const { |
+ const ResourceRequest& request = params.GetResourceRequest(); |
// Do not load from cache if images are not enabled. There are two general |
// cases: |
// |
@@ -899,13 +927,13 @@ ResourceFetcher::DetermineRevalidationPolicy( |
// TODO(japhet): Can we get rid of one of these settings? |
if (existing_resource->IsImage() && |
!Context().AllowImage(images_enabled_, existing_resource->Url())) { |
- return kReload; |
+ return false; |
} |
// Never use cache entries for downloadToFile / useStreamOnResponse requests. |
// The data will be delivered through other paths. |
if (request.DownloadToFile() || request.UseStreamOnResponse()) |
- return kReload; |
+ return false; |
// Never reuse opaque responses from a service worker for requests that are |
// not no-cors. https://crbug.com/625575 |
@@ -913,11 +941,11 @@ ResourceFetcher::DetermineRevalidationPolicy( |
existing_resource->GetResponse().ServiceWorkerResponseType() == |
kWebServiceWorkerResponseTypeOpaque && |
request.GetFetchRequestMode() != WebURLRequest::kFetchRequestModeNoCORS) { |
- return kReload; |
+ return false; |
} |
- if (!is_static_data && !existing_resource->CanReuse(fetch_params)) |
- return kReload; |
+ if (!is_static_data && !existing_resource->CanReuse(params)) |
+ return false; |
// Certain requests (e.g., XHRs) might have manually set headers that require |
// revalidation. In theory, this should be a Revalidate case. In practice, the |
@@ -933,17 +961,74 @@ ResourceFetcher::DetermineRevalidationPolicy( |
if (!is_static_data && |
(request.IsConditional() || |
existing_resource->GetResponse().HttpStatusCode() == 304)) { |
- return kReload; |
+ return false; |
} |
if (!is_static_data && |
- !fetch_params.Options().CanReuseRequest(existing_resource->Options())) { |
+ !params.Options().CanReuseRequest(existing_resource->Options())) { |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+ResourceFetcher::RevalidationPolicy |
+ResourceFetcher::DetermineRevalidationPolicy( |
+ Resource::Type type, |
+ const FetchParameters& fetch_params, |
+ Resource* existing_resource, |
+ bool is_static_data) const { |
+ const ResourceRequest& request = fetch_params.GetResourceRequest(); |
+ |
+ if (!existing_resource) |
+ return kLoad; |
+ |
+ // If the existing resource is loading and the associated fetcher is not equal |
+ // to |this|, we must not use the resource. Otherwise, CSP violation may |
+ // happen in redirect handling. |
+ if (existing_resource->Loader() && |
+ existing_resource->Loader()->Fetcher() != this) { |
return kReload; |
} |
- // Always use preloads. |
- if (existing_resource->IsPreloaded()) |
- return kUse; |
+ // Checks if the resource has an explicit policy about integrity metadata. |
+ // |
+ // This is necessary because ScriptResource and CSSStyleSheetResource objects |
+ // do not keep the raw data around after the source is accessed once, so if |
+ // the resource is accessed from the MemoryCache for a second time, there is |
+ // no way to redo an integrity check. |
+ // |
+ // Thus, Blink implements a scheme where it caches the integrity information |
+ // for those resources after the first time it is checked, and if there is |
+ // another request for that resource, with the same integrity metadata, Blink |
+ // skips the integrity calculation. However, if the integrity metadata is a |
+ // mismatch, the MemoryCache must be skipped here, and a new request for the |
+ // resource must be made to get the raw data. This is expected to be an |
+ // uncommon case, however, as it implies two same-origin requests to the same |
+ // resource, but with different integrity metadata. |
+ RecordSriResourceIntegrityMismatchEvent(kCheckingForIntegrityMismatch); |
+ if (existing_resource->MustRefetchDueToIntegrityMetadata(fetch_params)) { |
+ RecordSriResourceIntegrityMismatchEvent(kRefetchDueToIntegrityMismatch); |
+ return kReload; |
+ } |
+ |
+ // If the same URL has been loaded as a different type, we need to reload. |
+ if (existing_resource->GetType() != type) { |
+ // FIXME: If existingResource is a Preload and the new type is LinkPrefetch |
+ // We really should discard the new prefetch since the preload has more |
+ // specific type information! crbug.com/379893 |
+ // fast/dom/HTMLLinkElement/link-and-subresource-test hits this case. |
+ RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy " |
+ "reloading due to type mismatch."; |
+ return kReload; |
+ } |
+ |
+ // If |existing_resource| is not reusable as a preloaded resource, it should |
+ // not be reusable as a normal resource as well. |
+ if (!IsReusableAlsoForPreloading(fetch_params, existing_resource, |
+ is_static_data)) { |
+ return kReload; |
+ } |
// If resource was populated from a SubstituteData load or data: url, use it. |
if (is_static_data) |
@@ -1104,24 +1189,15 @@ int ResourceFetcher::NonblockingRequestCount() const { |
return non_blocking_loaders_.size(); |
} |
-void ResourceFetcher::PreloadStarted(Resource* resource) { |
- if (preloads_.Contains(resource)) |
- return; |
- resource->IncreasePreloadCount(); |
- |
- preloads_.insert(resource); |
- |
- if (preloaded_urls_for_test_) |
- preloaded_urls_for_test_->insert(resource->Url().GetString()); |
-} |
- |
void ResourceFetcher::EnableIsPreloadedForTest() { |
if (preloaded_urls_for_test_) |
return; |
preloaded_urls_for_test_ = WTF::WrapUnique(new HashSet<String>); |
- for (const auto& resource : preloads_) |
+ for (const auto& pair : preloads_) { |
+ Resource* resource = pair.value; |
preloaded_urls_for_test_->insert(resource->Url().GetString()); |
+ } |
} |
bool ResourceFetcher::IsPreloadedForTest(const KURL& url) const { |
@@ -1132,18 +1208,24 @@ bool ResourceFetcher::IsPreloadedForTest(const KURL& url) const { |
void ResourceFetcher::ClearPreloads(ClearPreloadsPolicy policy) { |
LogPreloadStats(policy); |
- for (const auto& resource : preloads_) { |
+ Vector<PreloadKey> keys_to_be_removed; |
+ for (const auto& pair : preloads_) { |
+ Resource* resource = pair.value; |
if (policy == kClearAllPreloads || !resource->IsLinkPreload()) { |
resource->DecreasePreloadCount(); |
if (resource->GetPreloadResult() == Resource::kPreloadNotReferenced) |
- GetMemoryCache()->Remove(resource.Get()); |
- preloads_.erase(resource); |
+ GetMemoryCache()->Remove(resource); |
+ keys_to_be_removed.push_back(pair.key); |
} |
} |
+ preloads_.RemoveAll(keys_to_be_removed); |
+ |
+ matched_preloads_.clear(); |
} |
void ResourceFetcher::WarnUnusedPreloads() { |
- for (const auto& resource : preloads_) { |
+ for (const auto& pair : preloads_) { |
+ Resource* resource = pair.value; |
if (resource && resource->IsLinkPreload() && |
resource->GetPreloadResult() == Resource::kPreloadNotReferenced) { |
Context().AddConsoleMessage( |
@@ -1250,6 +1332,8 @@ void ResourceFetcher::HandleLoaderError(Resource* resource, |
resource->GetResponse().EncodedDataLength(), |
is_internal_request); |
+ if (error.IsCancellation()) |
+ RemovePreload(resource); |
resource->FinishAsError(error); |
HandleLoadCompletion(resource); |
@@ -1409,7 +1493,8 @@ void ResourceFetcher::LogPreloadStats(ClearPreloadsPolicy policy) { |
unsigned import_misses = 0; |
unsigned raws = 0; |
unsigned raw_misses = 0; |
- for (const auto& resource : preloads_) { |
+ for (const auto& pair : preloads_) { |
+ Resource* resource = pair.value; |
// Do not double count link rel preloads. These do not get cleared if the |
// ClearPreloadsPolicy is only clearing speculative markup preloads. |
if (resource->IsLinkPreload() && |
@@ -1563,6 +1648,7 @@ DEFINE_TRACE(ResourceFetcher) { |
visitor->Trace(non_blocking_loaders_); |
visitor->Trace(document_resources_); |
visitor->Trace(preloads_); |
+ visitor->Trace(matched_preloads_); |
visitor->Trace(resource_timing_info_map_); |
} |