| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) | |
| 3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) | |
| 4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) | |
| 5 Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All | |
| 6 rights reserved. | |
| 7 Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ | |
| 8 | |
| 9 This library is free software; you can redistribute it and/or | |
| 10 modify it under the terms of the GNU Library General Public | |
| 11 License as published by the Free Software Foundation; either | |
| 12 version 2 of the License, or (at your option) any later version. | |
| 13 | |
| 14 This library is distributed in the hope that it will be useful, | |
| 15 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 17 Library General Public License for more details. | |
| 18 | |
| 19 You should have received a copy of the GNU Library General Public License | |
| 20 along with this library; see the file COPYING.LIB. If not, write to | |
| 21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
| 22 Boston, MA 02110-1301, USA. | |
| 23 | |
| 24 This class provides all functionality needed for loading images, style | |
| 25 sheets and html pages from the web. It has a memory cache for these objects. | |
| 26 */ | |
| 27 | |
| 28 #include "core/fetch/ResourceFetcher.h" | |
| 29 | |
| 30 #include "core/fetch/FetchContext.h" | |
| 31 #include "core/fetch/FetchInitiatorTypeNames.h" | |
| 32 #include "core/fetch/MemoryCache.h" | |
| 33 #include "core/fetch/ResourceLoader.h" | |
| 34 #include "core/fetch/ResourceLoadingLog.h" | |
| 35 #include "core/fetch/UniqueIdentifier.h" | |
| 36 #include "platform/Histogram.h" | |
| 37 #include "platform/RuntimeEnabledFeatures.h" | |
| 38 #include "platform/instrumentation/tracing/TraceEvent.h" | |
| 39 #include "platform/instrumentation/tracing/TracedValue.h" | |
| 40 #include "platform/mhtml/ArchiveResource.h" | |
| 41 #include "platform/mhtml/MHTMLArchive.h" | |
| 42 #include "platform/network/NetworkInstrumentation.h" | |
| 43 #include "platform/network/NetworkUtils.h" | |
| 44 #include "platform/network/ResourceTimingInfo.h" | |
| 45 #include "platform/weborigin/KnownPorts.h" | |
| 46 #include "platform/weborigin/SecurityOrigin.h" | |
| 47 #include "platform/weborigin/SecurityPolicy.h" | |
| 48 #include "public/platform/Platform.h" | |
| 49 #include "public/platform/WebCachePolicy.h" | |
| 50 #include "public/platform/WebURL.h" | |
| 51 #include "public/platform/WebURLRequest.h" | |
| 52 #include "wtf/text/CString.h" | |
| 53 #include "wtf/text/WTFString.h" | |
| 54 #include <memory> | |
| 55 | |
| 56 using blink::WebURLRequest; | |
| 57 | |
| 58 namespace blink { | |
| 59 | |
| 60 namespace { | |
| 61 | |
| 62 // Events for UMA. Do not reorder or delete. Add new events at the end, but | |
| 63 // before SriResourceIntegrityMismatchEventCount. | |
| 64 enum SriResourceIntegrityMismatchEvent { | |
| 65 CheckingForIntegrityMismatch = 0, | |
| 66 RefetchDueToIntegrityMismatch = 1, | |
| 67 SriResourceIntegrityMismatchEventCount | |
| 68 }; | |
| 69 | |
| 70 #define DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, name) \ | |
| 71 case Resource::name: { \ | |
| 72 DEFINE_THREAD_SAFE_STATIC_LOCAL( \ | |
| 73 EnumerationHistogram, resourceHistogram, \ | |
| 74 new EnumerationHistogram( \ | |
| 75 "Blink.MemoryCache.RevalidationPolicy." prefix #name, Load + 1)); \ | |
| 76 resourceHistogram.count(policy); \ | |
| 77 break; \ | |
| 78 } | |
| 79 | |
| 80 #define DEFINE_RESOURCE_HISTOGRAM(prefix) \ | |
| 81 switch (factory.type()) { \ | |
| 82 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, CSSStyleSheet) \ | |
| 83 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Font) \ | |
| 84 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Image) \ | |
| 85 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, ImportResource) \ | |
| 86 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, LinkPrefetch) \ | |
| 87 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, MainResource) \ | |
| 88 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Manifest) \ | |
| 89 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Media) \ | |
| 90 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Mock) \ | |
| 91 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Raw) \ | |
| 92 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, Script) \ | |
| 93 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, SVGDocument) \ | |
| 94 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, TextTrack) \ | |
| 95 DEFINE_SINGLE_RESOURCE_HISTOGRAM(prefix, XSLStyleSheet) \ | |
| 96 } | |
| 97 | |
| 98 void addRedirectsToTimingInfo(Resource* resource, ResourceTimingInfo* info) { | |
| 99 // Store redirect responses that were packed inside the final response. | |
| 100 const auto& responses = resource->response().redirectResponses(); | |
| 101 for (size_t i = 0; i < responses.size(); ++i) { | |
| 102 const KURL& newURL = i + 1 < responses.size() | |
| 103 ? KURL(responses[i + 1].url()) | |
| 104 : resource->resourceRequest().url(); | |
| 105 bool crossOrigin = | |
| 106 !SecurityOrigin::areSameSchemeHostPort(responses[i].url(), newURL); | |
| 107 info->addRedirect(responses[i], crossOrigin); | |
| 108 } | |
| 109 } | |
| 110 | |
| 111 void RecordSriResourceIntegrityMismatchEvent( | |
| 112 SriResourceIntegrityMismatchEvent event) { | |
| 113 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
| 114 EnumerationHistogram, integrityHistogram, | |
| 115 new EnumerationHistogram("sri.resource_integrity_mismatch_event", | |
| 116 SriResourceIntegrityMismatchEventCount)); | |
| 117 integrityHistogram.count(event); | |
| 118 } | |
| 119 | |
| 120 ResourceLoadPriority typeToPriority(Resource::Type type) { | |
| 121 switch (type) { | |
| 122 case Resource::MainResource: | |
| 123 case Resource::CSSStyleSheet: | |
| 124 case Resource::Font: | |
| 125 // Also parser-blocking scripts (set explicitly in loadPriority) | |
| 126 return ResourceLoadPriorityVeryHigh; | |
| 127 case Resource::XSLStyleSheet: | |
| 128 DCHECK(RuntimeEnabledFeatures::xsltEnabled()); | |
| 129 case Resource::Raw: | |
| 130 case Resource::ImportResource: | |
| 131 case Resource::Script: | |
| 132 // Also visible resources/images (set explicitly in loadPriority) | |
| 133 return ResourceLoadPriorityHigh; | |
| 134 case Resource::Manifest: | |
| 135 case Resource::Mock: | |
| 136 // Also late-body scripts discovered by the preload scanner (set | |
| 137 // explicitly in loadPriority) | |
| 138 return ResourceLoadPriorityMedium; | |
| 139 case Resource::Image: | |
| 140 case Resource::TextTrack: | |
| 141 case Resource::Media: | |
| 142 case Resource::SVGDocument: | |
| 143 // Also async scripts (set explicitly in loadPriority) | |
| 144 return ResourceLoadPriorityLow; | |
| 145 case Resource::LinkPrefetch: | |
| 146 return ResourceLoadPriorityVeryLow; | |
| 147 } | |
| 148 | |
| 149 NOTREACHED(); | |
| 150 return ResourceLoadPriorityUnresolved; | |
| 151 } | |
| 152 | |
| 153 } // namespace | |
| 154 | |
| 155 ResourceLoadPriority ResourceFetcher::computeLoadPriority( | |
| 156 Resource::Type type, | |
| 157 const FetchRequest& request, | |
| 158 ResourcePriority::VisibilityStatus visibility) { | |
| 159 ResourceLoadPriority priority = typeToPriority(type); | |
| 160 | |
| 161 // Visible resources (images in practice) get a boost to High priority. | |
| 162 if (visibility == ResourcePriority::Visible) | |
| 163 priority = ResourceLoadPriorityHigh; | |
| 164 | |
| 165 // Resources before the first image are considered "early" in the document and | |
| 166 // resources after the first image are "late" in the document. Important to | |
| 167 // note that this is based on when the preload scanner discovers a resource | |
| 168 // for the most part so the main parser may not have reached the image element | |
| 169 // yet. | |
| 170 if (type == Resource::Image) | |
| 171 m_imageFetched = true; | |
| 172 | |
| 173 if (FetchRequest::IdleLoad == request.defer()) { | |
| 174 priority = ResourceLoadPriorityVeryLow; | |
| 175 } else if (type == Resource::Script) { | |
| 176 // Special handling for scripts. | |
| 177 // Default/Parser-Blocking/Preload early in document: High (set in | |
| 178 // typeToPriority) | |
| 179 // Async/Defer: Low Priority (applies to both preload and parser-inserted) | |
| 180 // Preload late in document: Medium | |
| 181 if (FetchRequest::LazyLoad == request.defer()) | |
| 182 priority = ResourceLoadPriorityLow; | |
| 183 else if (request.forPreload() && m_imageFetched) | |
| 184 priority = ResourceLoadPriorityMedium; | |
| 185 } else if (FetchRequest::LazyLoad == request.defer()) { | |
| 186 priority = ResourceLoadPriorityVeryLow; | |
| 187 } | |
| 188 | |
| 189 // A manually set priority acts as a floor. This is used to ensure that | |
| 190 // synchronous requests are always given the highest possible priority, as | |
| 191 // well as to ensure that there isn't priority churn if images move in and out | |
| 192 // of the viewport, or is displayed more than once, both in and out of the | |
| 193 // viewport. | |
| 194 return std::max(context().modifyPriorityForExperiments(priority), | |
| 195 request.resourceRequest().priority()); | |
| 196 } | |
| 197 | |
| 198 static void populateTimingInfo(ResourceTimingInfo* info, | |
| 199 Resource* resource) { | |
| 200 KURL initialURL = resource->response().redirectResponses().isEmpty() | |
| 201 ? resource->resourceRequest().url() | |
| 202 : resource->response().redirectResponses()[0].url(); | |
| 203 info->setInitialURL(initialURL); | |
| 204 info->setFinalResponse(resource->response()); | |
| 205 } | |
| 206 | |
| 207 static WebURLRequest::RequestContext requestContextFromType( | |
| 208 bool isMainFrame, | |
| 209 Resource::Type type) { | |
| 210 switch (type) { | |
| 211 case Resource::MainResource: | |
| 212 if (!isMainFrame) | |
| 213 return WebURLRequest::RequestContextIframe; | |
| 214 // FIXME: Change this to a context frame type (once we introduce them): | |
| 215 // http://fetch.spec.whatwg.org/#concept-request-context-frame-type | |
| 216 return WebURLRequest::RequestContextHyperlink; | |
| 217 case Resource::XSLStyleSheet: | |
| 218 DCHECK(RuntimeEnabledFeatures::xsltEnabled()); | |
| 219 case Resource::CSSStyleSheet: | |
| 220 return WebURLRequest::RequestContextStyle; | |
| 221 case Resource::Script: | |
| 222 return WebURLRequest::RequestContextScript; | |
| 223 case Resource::Font: | |
| 224 return WebURLRequest::RequestContextFont; | |
| 225 case Resource::Image: | |
| 226 return WebURLRequest::RequestContextImage; | |
| 227 case Resource::Raw: | |
| 228 return WebURLRequest::RequestContextSubresource; | |
| 229 case Resource::ImportResource: | |
| 230 return WebURLRequest::RequestContextImport; | |
| 231 case Resource::LinkPrefetch: | |
| 232 return WebURLRequest::RequestContextPrefetch; | |
| 233 case Resource::TextTrack: | |
| 234 return WebURLRequest::RequestContextTrack; | |
| 235 case Resource::SVGDocument: | |
| 236 return WebURLRequest::RequestContextImage; | |
| 237 case Resource::Media: // TODO: Split this. | |
| 238 return WebURLRequest::RequestContextVideo; | |
| 239 case Resource::Manifest: | |
| 240 return WebURLRequest::RequestContextManifest; | |
| 241 case Resource::Mock: | |
| 242 return WebURLRequest::RequestContextSubresource; | |
| 243 } | |
| 244 NOTREACHED(); | |
| 245 return WebURLRequest::RequestContextSubresource; | |
| 246 } | |
| 247 | |
| 248 ResourceFetcher::ResourceFetcher(FetchContext* newContext) | |
| 249 : m_context(newContext), | |
| 250 m_archive(context().isMainFrame() ? nullptr : context().archive()), | |
| 251 m_resourceTimingReportTimer( | |
| 252 this, | |
| 253 &ResourceFetcher::resourceTimingReportTimerFired), | |
| 254 m_autoLoadImages(true), | |
| 255 m_imagesEnabled(true), | |
| 256 m_allowStaleResources(false), | |
| 257 m_imageFetched(false) { | |
| 258 } | |
| 259 | |
| 260 ResourceFetcher::~ResourceFetcher() {} | |
| 261 | |
| 262 Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const { | |
| 263 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL); | |
| 264 const WeakMember<Resource>& resource = m_documentResources.get(url); | |
| 265 return resource.get(); | |
| 266 } | |
| 267 | |
| 268 bool ResourceFetcher::isControlledByServiceWorker() const { | |
| 269 return context().isControlledByServiceWorker(); | |
| 270 } | |
| 271 | |
| 272 bool ResourceFetcher::resourceNeedsLoad(Resource* resource, | |
| 273 const FetchRequest& request, | |
| 274 RevalidationPolicy policy) { | |
| 275 // Defer a font load until it is actually needed unless this is a preload. | |
| 276 if (resource->getType() == Resource::Font && !request.forPreload()) | |
| 277 return false; | |
| 278 if (resource->isImage() && shouldDeferImageLoad(resource->url())) | |
| 279 return false; | |
| 280 return policy != Use || resource->stillNeedsLoad(); | |
| 281 } | |
| 282 | |
| 283 // Limit the number of URLs in m_validatedURLs to avoid memory bloat. | |
| 284 // http://crbug.com/52411 | |
| 285 static const int kMaxValidatedURLsSize = 10000; | |
| 286 | |
| 287 void ResourceFetcher::requestLoadStarted(unsigned long identifier, | |
| 288 Resource* resource, | |
| 289 const FetchRequest& request, | |
| 290 ResourceLoadStartType type, | |
| 291 bool isStaticData) { | |
| 292 if (type == ResourceLoadingFromCache && | |
| 293 resource->getStatus() == Resource::Cached && | |
| 294 !m_validatedURLs.contains(resource->url())) { | |
| 295 context().dispatchDidLoadResourceFromMemoryCache( | |
| 296 identifier, resource, request.resourceRequest().frameType(), | |
| 297 request.resourceRequest().requestContext()); | |
| 298 } | |
| 299 | |
| 300 if (isStaticData) | |
| 301 return; | |
| 302 | |
| 303 if (type == ResourceLoadingFromCache && !resource->stillNeedsLoad() && | |
| 304 !m_validatedURLs.contains(request.resourceRequest().url())) { | |
| 305 // Resources loaded from memory cache should be reported the first time | |
| 306 // they're used. | |
| 307 std::unique_ptr<ResourceTimingInfo> info = ResourceTimingInfo::create( | |
| 308 request.options().initiatorInfo.name, monotonicallyIncreasingTime(), | |
| 309 resource->getType() == Resource::MainResource); | |
| 310 populateTimingInfo(info.get(), resource); | |
| 311 info->clearLoadTimings(); | |
| 312 info->setLoadFinishTime(info->initialTime()); | |
| 313 m_scheduledResourceTimingReports.push_back(std::move(info)); | |
| 314 if (!m_resourceTimingReportTimer.isActive()) | |
| 315 m_resourceTimingReportTimer.startOneShot(0, BLINK_FROM_HERE); | |
| 316 } | |
| 317 | |
| 318 if (m_validatedURLs.size() >= kMaxValidatedURLsSize) { | |
| 319 m_validatedURLs.clear(); | |
| 320 } | |
| 321 m_validatedURLs.add(request.resourceRequest().url()); | |
| 322 } | |
| 323 | |
| 324 static std::unique_ptr<TracedValue> urlForTraceEvent(const KURL& url) { | |
| 325 std::unique_ptr<TracedValue> value = TracedValue::create(); | |
| 326 value->setString("url", url.getString()); | |
| 327 return value; | |
| 328 } | |
| 329 | |
| 330 Resource* ResourceFetcher::resourceForStaticData( | |
| 331 const FetchRequest& request, | |
| 332 const ResourceFactory& factory, | |
| 333 const SubstituteData& substituteData) { | |
| 334 const KURL& url = request.resourceRequest().url(); | |
| 335 DCHECK(url.protocolIsData() || substituteData.isValid() || m_archive); | |
| 336 | |
| 337 // TODO(japhet): We only send main resource data: urls through WebURLLoader | |
| 338 // for the benefit of a service worker test | |
| 339 // (RenderViewImplTest.ServiceWorkerNetworkProviderSetup), which is at a layer | |
| 340 // where it isn't easy to mock out a network load. It uses data: urls to | |
| 341 // emulate the behavior it wants to test, which would otherwise be reserved | |
| 342 // for network loads. | |
| 343 if (!m_archive && !substituteData.isValid() && | |
| 344 (factory.type() == Resource::MainResource || | |
| 345 factory.type() == Resource::Raw)) | |
| 346 return nullptr; | |
| 347 | |
| 348 const String cacheIdentifier = getCacheIdentifier(); | |
| 349 if (Resource* oldResource = | |
| 350 memoryCache()->resourceForURL(url, cacheIdentifier)) { | |
| 351 // There's no reason to re-parse if we saved the data from the previous | |
| 352 // parse. | |
| 353 if (request.options().dataBufferingPolicy != DoNotBufferData) | |
| 354 return oldResource; | |
| 355 memoryCache()->remove(oldResource); | |
| 356 } | |
| 357 | |
| 358 AtomicString mimetype; | |
| 359 AtomicString charset; | |
| 360 RefPtr<SharedBuffer> data; | |
| 361 if (substituteData.isValid()) { | |
| 362 mimetype = substituteData.mimeType(); | |
| 363 charset = substituteData.textEncoding(); | |
| 364 data = substituteData.content(); | |
| 365 } else if (url.protocolIsData()) { | |
| 366 data = PassRefPtr<SharedBuffer>( | |
| 367 NetworkUtils::parseDataURL(url, mimetype, charset)); | |
| 368 if (!data) | |
| 369 return nullptr; | |
| 370 } else { | |
| 371 ArchiveResource* archiveResource = | |
| 372 m_archive->subresourceForURL(request.url()); | |
| 373 // Fall back to the network if the archive doesn't contain the resource. | |
| 374 if (!archiveResource) | |
| 375 return nullptr; | |
| 376 mimetype = archiveResource->mimeType(); | |
| 377 charset = archiveResource->textEncoding(); | |
| 378 data = archiveResource->data(); | |
| 379 } | |
| 380 | |
| 381 ResourceResponse response(url, mimetype, data->size(), charset, String()); | |
| 382 response.setHTTPStatusCode(200); | |
| 383 response.setHTTPStatusText("OK"); | |
| 384 | |
| 385 Resource* resource = factory.create(request.resourceRequest(), | |
| 386 request.options(), request.charset()); | |
| 387 resource->setNeedsSynchronousCacheHit(substituteData.forceSynchronousLoad()); | |
| 388 // FIXME: We should provide a body stream here. | |
| 389 resource->responseReceived(response, nullptr); | |
| 390 resource->setDataBufferingPolicy(BufferData); | |
| 391 if (data->size()) | |
| 392 resource->setResourceBuffer(data); | |
| 393 resource->setIdentifier(createUniqueIdentifier()); | |
| 394 resource->setCacheIdentifier(cacheIdentifier); | |
| 395 resource->finish(); | |
| 396 | |
| 397 if (!substituteData.isValid()) | |
| 398 memoryCache()->add(resource); | |
| 399 | |
| 400 return resource; | |
| 401 } | |
| 402 | |
| 403 Resource* ResourceFetcher::resourceForBlockedRequest( | |
| 404 const FetchRequest& request, | |
| 405 const ResourceFactory& factory, | |
| 406 ResourceRequestBlockedReason blockedReason) { | |
| 407 Resource* resource = factory.create(request.resourceRequest(), | |
| 408 request.options(), request.charset()); | |
| 409 resource->error(ResourceError::cancelledDueToAccessCheckError(request.url(), | |
| 410 blockedReason)); | |
| 411 return resource; | |
| 412 } | |
| 413 | |
| 414 void ResourceFetcher::moveCachedNonBlockingResourceToBlocking( | |
| 415 Resource* resource, | |
| 416 const FetchRequest& request) { | |
| 417 // TODO(yoav): Test that non-blocking resources (video/audio/track) continue | |
| 418 // to not-block even after being preloaded and discovered. | |
| 419 if (resource && resource->loader() && | |
| 420 resource->isLoadEventBlockingResourceType() && | |
| 421 m_nonBlockingLoaders.contains(resource->loader()) && | |
| 422 resource->isLinkPreload() && !request.forPreload()) { | |
| 423 m_nonBlockingLoaders.remove(resource->loader()); | |
| 424 m_loaders.add(resource->loader()); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 void ResourceFetcher::updateMemoryCacheStats(Resource* resource, | |
| 429 RevalidationPolicy policy, | |
| 430 const FetchRequest& request, | |
| 431 const ResourceFactory& factory, | |
| 432 bool isStaticData) const { | |
| 433 if (isStaticData) | |
| 434 return; | |
| 435 | |
| 436 if (request.forPreload()) { | |
| 437 DEFINE_RESOURCE_HISTOGRAM("Preload."); | |
| 438 } else { | |
| 439 DEFINE_RESOURCE_HISTOGRAM(""); | |
| 440 } | |
| 441 | |
| 442 // Aims to count Resource only referenced from MemoryCache (i.e. what would be | |
| 443 // dead if MemoryCache holds weak references to Resource). Currently we check | |
| 444 // references to Resource from ResourceClient and |m_preloads| only, because | |
| 445 // they are major sources of references. | |
| 446 if (resource && !resource->isAlive() && | |
| 447 (!m_preloads || !m_preloads->contains(resource))) { | |
| 448 DEFINE_RESOURCE_HISTOGRAM("Dead."); | |
| 449 } | |
| 450 } | |
| 451 | |
| 452 Resource* ResourceFetcher::requestResource( | |
| 453 FetchRequest& request, | |
| 454 const ResourceFactory& factory, | |
| 455 const SubstituteData& substituteData) { | |
| 456 ResourceRequest& resourceRequest = request.mutableResourceRequest(); | |
| 457 | |
| 458 unsigned long identifier = createUniqueIdentifier(); | |
| 459 network_instrumentation::ScopedResourceLoadTracker scopedResourceLoadTracker( | |
| 460 identifier, resourceRequest); | |
| 461 SCOPED_BLINK_UMA_HISTOGRAM_TIMER("Blink.Fetch.RequestResourceTime"); | |
| 462 DCHECK(request.options().synchronousPolicy == RequestAsynchronously || | |
| 463 factory.type() == Resource::Raw || | |
| 464 factory.type() == Resource::XSLStyleSheet); | |
| 465 | |
| 466 context().populateResourceRequest( | |
| 467 factory.type(), request.clientHintsPreferences(), | |
| 468 request.getResourceWidth(), resourceRequest); | |
| 469 | |
| 470 // TODO(dproy): Remove this. http://crbug.com/659666 | |
| 471 TRACE_EVENT1("blink", "ResourceFetcher::requestResource", "url", | |
| 472 urlForTraceEvent(request.url())); | |
| 473 | |
| 474 if (!request.url().isValid()) | |
| 475 return nullptr; | |
| 476 | |
| 477 resourceRequest.setPriority(computeLoadPriority( | |
| 478 factory.type(), request, ResourcePriority::NotVisible)); | |
| 479 initializeResourceRequest(resourceRequest, factory.type(), request.defer()); | |
| 480 network_instrumentation::resourcePrioritySet(identifier, | |
| 481 resourceRequest.priority()); | |
| 482 | |
| 483 ResourceRequestBlockedReason blockedReason = context().canRequest( | |
| 484 factory.type(), resourceRequest, | |
| 485 MemoryCache::removeFragmentIdentifierIfNeeded(request.url()), | |
| 486 request.options(), request.forPreload(), request.getOriginRestriction()); | |
| 487 if (blockedReason != ResourceRequestBlockedReason::None) { | |
| 488 DCHECK(!substituteData.forceSynchronousLoad()); | |
| 489 return resourceForBlockedRequest(request, factory, blockedReason); | |
| 490 } | |
| 491 | |
| 492 context().willStartLoadingResource( | |
| 493 identifier, resourceRequest, factory.type(), | |
| 494 request.options().initiatorInfo.name, request.forPreload()); | |
| 495 if (!request.url().isValid()) | |
| 496 return nullptr; | |
| 497 | |
| 498 bool isDataUrl = resourceRequest.url().protocolIsData(); | |
| 499 bool isStaticData = isDataUrl || substituteData.isValid() || m_archive; | |
| 500 Resource* resource(nullptr); | |
| 501 if (isStaticData) { | |
| 502 resource = resourceForStaticData(request, factory, substituteData); | |
| 503 // Abort the request if the archive doesn't contain the resource, except in | |
| 504 // the case of data URLs which might have resources such as fonts that need | |
| 505 // to be decoded only on demand. These data URLs are allowed to be | |
| 506 // processed using the normal ResourceFetcher machinery. | |
| 507 if (!resource && !isDataUrl && m_archive) | |
| 508 return nullptr; | |
| 509 } | |
| 510 if (!resource) { | |
| 511 resource = | |
| 512 memoryCache()->resourceForURL(request.url(), getCacheIdentifier()); | |
| 513 } | |
| 514 | |
| 515 // See if we can use an existing resource from the cache. If so, we need to | |
| 516 // move it to be load blocking. | |
| 517 moveCachedNonBlockingResourceToBlocking(resource, request); | |
| 518 | |
| 519 const RevalidationPolicy policy = determineRevalidationPolicy( | |
| 520 factory.type(), request, resource, isStaticData); | |
| 521 TRACE_EVENT_INSTANT1("blink", "ResourceFetcher::determineRevalidationPolicy", | |
| 522 TRACE_EVENT_SCOPE_THREAD, "revalidationPolicy", policy); | |
| 523 | |
| 524 updateMemoryCacheStats(resource, policy, request, factory, isStaticData); | |
| 525 | |
| 526 resourceRequest.setAllowStoredCredentials( | |
| 527 request.options().allowCredentials == AllowStoredCredentials); | |
| 528 | |
| 529 switch (policy) { | |
| 530 case Reload: | |
| 531 memoryCache()->remove(resource); | |
| 532 // Fall through | |
| 533 case Load: | |
| 534 resource = createResourceForLoading(request, request.charset(), factory); | |
| 535 break; | |
| 536 case Revalidate: | |
| 537 initializeRevalidation(resourceRequest, resource); | |
| 538 break; | |
| 539 case Use: | |
| 540 if (resource->isLinkPreload() && !request.isLinkPreload()) | |
| 541 resource->setLinkPreload(false); | |
| 542 break; | |
| 543 } | |
| 544 if (!resource) | |
| 545 return nullptr; | |
| 546 if (resource->getType() != factory.type()) { | |
| 547 DCHECK(request.forPreload()); | |
| 548 return nullptr; | |
| 549 } | |
| 550 | |
| 551 if (!resource->isAlive()) | |
| 552 m_deadStatsRecorder.update(policy); | |
| 553 | |
| 554 if (policy != Use) | |
| 555 resource->setIdentifier(identifier); | |
| 556 | |
| 557 if (!request.forPreload() || policy != Use) { | |
| 558 // When issuing another request for a resource that is already in-flight | |
| 559 // make sure to not demote the priority of the in-flight request. If the new | |
| 560 // request isn't at the same priority as the in-flight request, only allow | |
| 561 // promotions. This can happen when a visible image's priority is increased | |
| 562 // and then another reference to the image is parsed (which would be at a | |
| 563 // lower priority). | |
| 564 if (resourceRequest.priority() > resource->resourceRequest().priority()) | |
| 565 resource->didChangePriority(resourceRequest.priority(), 0); | |
| 566 } | |
| 567 | |
| 568 // If only the fragment identifiers differ, it is the same resource. | |
| 569 DCHECK(equalIgnoringFragmentIdentifier(resource->url(), request.url())); | |
| 570 requestLoadStarted( | |
| 571 identifier, resource, request, | |
| 572 policy == Use ? ResourceLoadingFromCache : ResourceLoadingFromNetwork, | |
| 573 isStaticData); | |
| 574 m_documentResources.set( | |
| 575 MemoryCache::removeFragmentIdentifierIfNeeded(request.url()), resource); | |
| 576 | |
| 577 // Returns with an existing resource if the resource does not need to start | |
| 578 // loading immediately. If revalidation policy was determined as |Revalidate|, | |
| 579 // the resource was already initialized for the revalidation here, but won't | |
| 580 // start loading. | |
| 581 if (!resourceNeedsLoad(resource, request, policy)) | |
| 582 return resource; | |
| 583 | |
| 584 if (!startLoad(resource)) | |
| 585 return nullptr; | |
| 586 scopedResourceLoadTracker.resourceLoadContinuesBeyondScope(); | |
| 587 | |
| 588 DCHECK(!resource->errorOccurred() || | |
| 589 request.options().synchronousPolicy == RequestSynchronously); | |
| 590 return resource; | |
| 591 } | |
| 592 | |
| 593 void ResourceFetcher::resourceTimingReportTimerFired(TimerBase* timer) { | |
| 594 DCHECK_EQ(timer, &m_resourceTimingReportTimer); | |
| 595 Vector<std::unique_ptr<ResourceTimingInfo>> timingReports; | |
| 596 timingReports.swap(m_scheduledResourceTimingReports); | |
| 597 for (const auto& timingInfo : timingReports) | |
| 598 context().addResourceTiming(*timingInfo); | |
| 599 } | |
| 600 | |
| 601 void ResourceFetcher::determineRequestContext(ResourceRequest& request, | |
| 602 Resource::Type type, | |
| 603 bool isMainFrame) { | |
| 604 WebURLRequest::RequestContext requestContext = | |
| 605 requestContextFromType(isMainFrame, type); | |
| 606 request.setRequestContext(requestContext); | |
| 607 } | |
| 608 | |
| 609 void ResourceFetcher::determineRequestContext(ResourceRequest& request, | |
| 610 Resource::Type type) { | |
| 611 determineRequestContext(request, type, context().isMainFrame()); | |
| 612 } | |
| 613 | |
| 614 void ResourceFetcher::initializeResourceRequest( | |
| 615 ResourceRequest& request, | |
| 616 Resource::Type type, | |
| 617 FetchRequest::DeferOption defer) { | |
| 618 if (request.getCachePolicy() == WebCachePolicy::UseProtocolCachePolicy) { | |
| 619 request.setCachePolicy( | |
| 620 context().resourceRequestCachePolicy(request, type, defer)); | |
| 621 } | |
| 622 if (request.requestContext() == WebURLRequest::RequestContextUnspecified) | |
| 623 determineRequestContext(request, type); | |
| 624 if (type == Resource::LinkPrefetch) | |
| 625 request.setHTTPHeaderField(HTTPNames::Purpose, "prefetch"); | |
| 626 | |
| 627 context().addAdditionalRequestHeaders( | |
| 628 request, | |
| 629 (type == Resource::MainResource) ? FetchMainResource : FetchSubresource); | |
| 630 } | |
| 631 | |
| 632 void ResourceFetcher::initializeRevalidation( | |
| 633 ResourceRequest& revalidatingRequest, | |
| 634 Resource* resource) { | |
| 635 DCHECK(resource); | |
| 636 DCHECK(memoryCache()->contains(resource)); | |
| 637 DCHECK(resource->isLoaded()); | |
| 638 DCHECK(resource->canUseCacheValidator()); | |
| 639 DCHECK(!resource->isCacheValidator()); | |
| 640 DCHECK(!context().isControlledByServiceWorker()); | |
| 641 | |
| 642 const AtomicString& lastModified = | |
| 643 resource->response().httpHeaderField(HTTPNames::Last_Modified); | |
| 644 const AtomicString& eTag = | |
| 645 resource->response().httpHeaderField(HTTPNames::ETag); | |
| 646 if (!lastModified.isEmpty() || !eTag.isEmpty()) { | |
| 647 DCHECK_NE(context().getCachePolicy(), CachePolicyReload); | |
| 648 if (context().getCachePolicy() == CachePolicyRevalidate) { | |
| 649 revalidatingRequest.setHTTPHeaderField(HTTPNames::Cache_Control, | |
| 650 "max-age=0"); | |
| 651 } | |
| 652 } | |
| 653 if (!lastModified.isEmpty()) { | |
| 654 revalidatingRequest.setHTTPHeaderField(HTTPNames::If_Modified_Since, | |
| 655 lastModified); | |
| 656 } | |
| 657 if (!eTag.isEmpty()) | |
| 658 revalidatingRequest.setHTTPHeaderField(HTTPNames::If_None_Match, eTag); | |
| 659 | |
| 660 double stalenessLifetime = resource->stalenessLifetime(); | |
| 661 if (std::isfinite(stalenessLifetime) && stalenessLifetime > 0) { | |
| 662 revalidatingRequest.setHTTPHeaderField( | |
| 663 HTTPNames::Resource_Freshness, | |
| 664 AtomicString(String::format( | |
| 665 "max-age=%.0lf,stale-while-revalidate=%.0lf,age=%.0lf", | |
| 666 resource->freshnessLifetime(), stalenessLifetime, | |
| 667 resource->currentAge()))); | |
| 668 } | |
| 669 | |
| 670 resource->setRevalidatingRequest(revalidatingRequest); | |
| 671 } | |
| 672 | |
| 673 Resource* ResourceFetcher::createResourceForLoading( | |
| 674 FetchRequest& request, | |
| 675 const String& charset, | |
| 676 const ResourceFactory& factory) { | |
| 677 const String cacheIdentifier = getCacheIdentifier(); | |
| 678 DCHECK(!memoryCache()->resourceForURL(request.resourceRequest().url(), | |
| 679 cacheIdentifier)); | |
| 680 | |
| 681 RESOURCE_LOADING_DVLOG(1) << "Loading Resource for " | |
| 682 << request.resourceRequest().url().elidedString(); | |
| 683 | |
| 684 Resource* resource = | |
| 685 factory.create(request.resourceRequest(), request.options(), charset); | |
| 686 resource->setLinkPreload(request.isLinkPreload()); | |
| 687 if (request.forPreload()) { | |
| 688 resource->setPreloadDiscoveryTime(request.preloadDiscoveryTime()); | |
| 689 } | |
| 690 resource->setCacheIdentifier(cacheIdentifier); | |
| 691 | |
| 692 // - Don't add main resource to cache to prevent reuse. | |
| 693 // - Don't add the resource if its body will not be stored. | |
| 694 if (factory.type() != Resource::MainResource && | |
| 695 request.options().dataBufferingPolicy != DoNotBufferData) { | |
| 696 memoryCache()->add(resource); | |
| 697 } | |
| 698 return resource; | |
| 699 } | |
| 700 | |
| 701 void ResourceFetcher::storePerformanceTimingInitiatorInformation( | |
| 702 Resource* resource) { | |
| 703 const AtomicString& fetchInitiator = resource->options().initiatorInfo.name; | |
| 704 if (fetchInitiator == FetchInitiatorTypeNames::internal) | |
| 705 return; | |
| 706 | |
| 707 bool isMainResource = resource->getType() == Resource::MainResource; | |
| 708 | |
| 709 // The request can already be fetched in a previous navigation. Thus | |
| 710 // startTime must be set accordingly. | |
| 711 double startTime = resource->resourceRequest().navigationStartTime() | |
| 712 ? resource->resourceRequest().navigationStartTime() | |
| 713 : monotonicallyIncreasingTime(); | |
| 714 | |
| 715 // This buffer is created and populated for providing transferSize | |
| 716 // and redirect timing opt-in information. | |
| 717 if (isMainResource) { | |
| 718 DCHECK(!m_navigationTimingInfo); | |
| 719 m_navigationTimingInfo = | |
| 720 ResourceTimingInfo::create(fetchInitiator, startTime, isMainResource); | |
| 721 } | |
| 722 | |
| 723 std::unique_ptr<ResourceTimingInfo> info = | |
| 724 ResourceTimingInfo::create(fetchInitiator, startTime, isMainResource); | |
| 725 | |
| 726 if (resource->isCacheValidator()) { | |
| 727 const AtomicString& timingAllowOrigin = | |
| 728 resource->response().httpHeaderField(HTTPNames::Timing_Allow_Origin); | |
| 729 if (!timingAllowOrigin.isEmpty()) | |
| 730 info->setOriginalTimingAllowOrigin(timingAllowOrigin); | |
| 731 } | |
| 732 | |
| 733 if (!isMainResource || | |
| 734 context().updateTimingInfoForIFrameNavigation(info.get())) { | |
| 735 m_resourceTimingInfoMap.add(resource, std::move(info)); | |
| 736 } | |
| 737 } | |
| 738 | |
| 739 void ResourceFetcher::recordResourceTimingOnRedirect( | |
| 740 Resource* resource, | |
| 741 const ResourceResponse& redirectResponse, | |
| 742 bool crossOrigin) { | |
| 743 ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource); | |
| 744 if (it != m_resourceTimingInfoMap.end()) { | |
| 745 it->value->addRedirect(redirectResponse, crossOrigin); | |
| 746 } | |
| 747 | |
| 748 if (resource->getType() == Resource::MainResource) { | |
| 749 DCHECK(m_navigationTimingInfo); | |
| 750 m_navigationTimingInfo->addRedirect(redirectResponse, crossOrigin); | |
| 751 } | |
| 752 } | |
| 753 | |
| 754 ResourceFetcher::RevalidationPolicy | |
| 755 ResourceFetcher::determineRevalidationPolicy(Resource::Type type, | |
| 756 const FetchRequest& fetchRequest, | |
| 757 Resource* existingResource, | |
| 758 bool isStaticData) const { | |
| 759 const ResourceRequest& request = fetchRequest.resourceRequest(); | |
| 760 | |
| 761 if (!existingResource) | |
| 762 return Load; | |
| 763 | |
| 764 // Checks if the resource has an explicit policy about integrity metadata. | |
| 765 // | |
| 766 // This is necessary because ScriptResource and CSSStyleSheetResource objects | |
| 767 // do not keep the raw data around after the source is accessed once, so if | |
| 768 // the resource is accessed from the MemoryCache for a second time, there is | |
| 769 // no way to redo an integrity check. | |
| 770 // | |
| 771 // Thus, Blink implements a scheme where it caches the integrity information | |
| 772 // for those resources after the first time it is checked, and if there is | |
| 773 // another request for that resource, with the same integrity metadata, Blink | |
| 774 // skips the integrity calculation. However, if the integrity metadata is a | |
| 775 // mismatch, the MemoryCache must be skipped here, and a new request for the | |
| 776 // resource must be made to get the raw data. This is expected to be an | |
| 777 // uncommon case, however, as it implies two same-origin requests to the same | |
| 778 // resource, but with different integrity metadata. | |
| 779 RecordSriResourceIntegrityMismatchEvent(CheckingForIntegrityMismatch); | |
| 780 if (existingResource->mustRefetchDueToIntegrityMetadata(fetchRequest)) { | |
| 781 RecordSriResourceIntegrityMismatchEvent(RefetchDueToIntegrityMismatch); | |
| 782 return Reload; | |
| 783 } | |
| 784 | |
| 785 // Service Worker's CORS fallback message must not be cached. | |
| 786 if (existingResource->response().wasFallbackRequiredByServiceWorker()) | |
| 787 return Reload; | |
| 788 | |
| 789 // We already have a preload going for this URL. | |
| 790 if (fetchRequest.forPreload() && existingResource->isPreloaded()) | |
| 791 return Use; | |
| 792 | |
| 793 // If the same URL has been loaded as a different type, we need to reload. | |
| 794 if (existingResource->getType() != type) { | |
| 795 // FIXME: If existingResource is a Preload and the new type is LinkPrefetch | |
| 796 // We really should discard the new prefetch since the preload has more | |
| 797 // specific type information! crbug.com/379893 | |
| 798 // fast/dom/HTMLLinkElement/link-and-subresource-test hits this case. | |
| 799 RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy " | |
| 800 "reloading due to type mismatch."; | |
| 801 return Reload; | |
| 802 } | |
| 803 | |
| 804 // Do not load from cache if images are not enabled. There are two general | |
| 805 // cases: | |
| 806 // | |
| 807 // 1. Images are disabled. Don't ever load images, even if the image is cached | |
| 808 // or it is a data: url. In this case, we "Reload" the image, then defer it | |
| 809 // with resourceNeedsLoad() so that it never actually goes to the network. | |
| 810 // | |
| 811 // 2. Images are enabled, but not loaded automatically. In this case, we will | |
| 812 // Use cached resources or data: urls, but will similarly fall back to a | |
| 813 // deferred network load if we don't have the data available without a network | |
| 814 // request. We check allowImage() here, which is affected by m_imagesEnabled | |
| 815 // but not m_autoLoadImages, in order to allow for this differing behavior. | |
| 816 // | |
| 817 // TODO(japhet): Can we get rid of one of these settings? | |
| 818 if (existingResource->isImage() && | |
| 819 !context().allowImage(m_imagesEnabled, existingResource->url())) | |
| 820 return Reload; | |
| 821 | |
| 822 // Never use cache entries for downloadToFile / useStreamOnResponse requests. | |
| 823 // The data will be delivered through other paths. | |
| 824 if (request.downloadToFile() || request.useStreamOnResponse()) | |
| 825 return Reload; | |
| 826 | |
| 827 // Never reuse opaque responses from a service worker for requests that are | |
| 828 // not no-cors. https://crbug.com/625575 | |
| 829 if (existingResource->response().wasFetchedViaServiceWorker() && | |
| 830 existingResource->response().serviceWorkerResponseType() == | |
| 831 WebServiceWorkerResponseTypeOpaque && | |
| 832 request.fetchRequestMode() != WebURLRequest::FetchRequestModeNoCORS) | |
| 833 return Reload; | |
| 834 | |
| 835 // If resource was populated from a SubstituteData load or data: url, use it. | |
| 836 if (isStaticData) | |
| 837 return Use; | |
| 838 | |
| 839 if (!existingResource->canReuse(request)) | |
| 840 return Reload; | |
| 841 | |
| 842 // Certain requests (e.g., XHRs) might have manually set headers that require | |
| 843 // revalidation. In theory, this should be a Revalidate case. In practice, the | |
| 844 // MemoryCache revalidation path assumes a whole bunch of things about how | |
| 845 // revalidation works that manual headers violate, so punt to Reload instead. | |
| 846 // | |
| 847 // Similarly, a request with manually added revalidation headers can lead to a | |
| 848 // 304 response for a request that wasn't flagged as a revalidation attempt. | |
| 849 // Normally, successful revalidation will maintain the original response's | |
| 850 // status code, but for a manual revalidation the response code remains 304. | |
| 851 // In this case, the Resource likely has insufficient context to provide a | |
| 852 // useful cache hit or revalidation. See http://crbug.com/643659 | |
| 853 if (request.isConditional() || | |
| 854 existingResource->response().httpStatusCode() == 304) | |
| 855 return Reload; | |
| 856 | |
| 857 // Don't reload resources while pasting. | |
| 858 if (m_allowStaleResources) | |
| 859 return Use; | |
| 860 | |
| 861 if (!fetchRequest.options().canReuseRequest(existingResource->options())) | |
| 862 return Reload; | |
| 863 | |
| 864 // Always use preloads. | |
| 865 if (existingResource->isPreloaded()) | |
| 866 return Use; | |
| 867 | |
| 868 // CachePolicyHistoryBuffer uses the cache no matter what. | |
| 869 CachePolicy cachePolicy = context().getCachePolicy(); | |
| 870 if (cachePolicy == CachePolicyHistoryBuffer) | |
| 871 return Use; | |
| 872 | |
| 873 // Don't reuse resources with Cache-control: no-store. | |
| 874 if (existingResource->hasCacheControlNoStoreHeader()) { | |
| 875 RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy " | |
| 876 "reloading due to Cache-control: no-store."; | |
| 877 return Reload; | |
| 878 } | |
| 879 | |
| 880 // If credentials were sent with the previous request and won't be with this | |
| 881 // one, or vice versa, re-fetch the resource. | |
| 882 // | |
| 883 // This helps with the case where the server sends back | |
| 884 // "Access-Control-Allow-Origin: *" all the time, but some of the client's | |
| 885 // requests are made without CORS and some with. | |
| 886 if (existingResource->resourceRequest().allowStoredCredentials() != | |
| 887 request.allowStoredCredentials()) { | |
| 888 RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy " | |
| 889 "reloading due to difference in credentials " | |
| 890 "settings."; | |
| 891 return Reload; | |
| 892 } | |
| 893 | |
| 894 // During the initial load, avoid loading the same resource multiple times for | |
| 895 // a single document, even if the cache policies would tell us to. We also | |
| 896 // group loads of the same resource together. Raw resources are exempted, as | |
| 897 // XHRs fall into this category and may have user-set Cache-Control: headers | |
| 898 // or other factors that require separate requests. | |
| 899 if (type != Resource::Raw) { | |
| 900 if (!context().isLoadComplete() && | |
| 901 m_validatedURLs.contains(existingResource->url())) | |
| 902 return Use; | |
| 903 if (existingResource->isLoading()) | |
| 904 return Use; | |
| 905 } | |
| 906 | |
| 907 if (request.getCachePolicy() == WebCachePolicy::BypassingCache) | |
| 908 return Reload; | |
| 909 | |
| 910 // CachePolicyReload always reloads | |
| 911 if (cachePolicy == CachePolicyReload) { | |
| 912 RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy " | |
| 913 "reloading due to CachePolicyReload."; | |
| 914 return Reload; | |
| 915 } | |
| 916 | |
| 917 // We'll try to reload the resource if it failed last time. | |
| 918 if (existingResource->errorOccurred()) { | |
| 919 RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::" | |
| 920 "determineRevalidationPolicye reloading due " | |
| 921 "to resource being in the error state"; | |
| 922 return Reload; | |
| 923 } | |
| 924 | |
| 925 // List of available images logic allows images to be re-used without cache | |
| 926 // validation. We restrict this only to images from memory cache which are the | |
| 927 // same as the version in the current document. | |
| 928 if (type == Resource::Image && | |
| 929 existingResource == cachedResource(request.url())) | |
| 930 return Use; | |
| 931 | |
| 932 // Defer to the browser process cache for Vary header handling. | |
| 933 if (existingResource->hasVaryHeader()) | |
| 934 return Reload; | |
| 935 | |
| 936 // If any of the redirects in the chain to loading the resource were not | |
| 937 // cacheable, we cannot reuse our cached resource. | |
| 938 if (!existingResource->canReuseRedirectChain()) { | |
| 939 RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy " | |
| 940 "reloading due to an uncacheable redirect"; | |
| 941 return Reload; | |
| 942 } | |
| 943 | |
| 944 // Check if the cache headers requires us to revalidate (cache expiration for | |
| 945 // example). | |
| 946 if (cachePolicy == CachePolicyRevalidate || | |
| 947 existingResource->mustRevalidateDueToCacheHeaders() || | |
| 948 request.cacheControlContainsNoCache()) { | |
| 949 // See if the resource has usable ETag or Last-modified headers. If the page | |
| 950 // is controlled by the ServiceWorker, we choose the Reload policy because | |
| 951 // the revalidation headers should not be exposed to the | |
| 952 // ServiceWorker.(crbug.com/429570) | |
| 953 if (existingResource->canUseCacheValidator() && | |
| 954 !context().isControlledByServiceWorker()) { | |
| 955 // If the resource is already a cache validator but not started yet, the | |
| 956 // |Use| policy should be applied to subsequent requests. | |
| 957 if (existingResource->isCacheValidator()) { | |
| 958 DCHECK(existingResource->stillNeedsLoad()); | |
| 959 return Use; | |
| 960 } | |
| 961 return Revalidate; | |
| 962 } | |
| 963 | |
| 964 // No, must reload. | |
| 965 RESOURCE_LOADING_DVLOG(1) << "ResourceFetcher::determineRevalidationPolicy " | |
| 966 "reloading due to missing cache validators."; | |
| 967 return Reload; | |
| 968 } | |
| 969 | |
| 970 return Use; | |
| 971 } | |
| 972 | |
| 973 void ResourceFetcher::setAutoLoadImages(bool enable) { | |
| 974 if (enable == m_autoLoadImages) | |
| 975 return; | |
| 976 | |
| 977 m_autoLoadImages = enable; | |
| 978 | |
| 979 if (!m_autoLoadImages) | |
| 980 return; | |
| 981 | |
| 982 reloadImagesIfNotDeferred(); | |
| 983 } | |
| 984 | |
| 985 void ResourceFetcher::setImagesEnabled(bool enable) { | |
| 986 if (enable == m_imagesEnabled) | |
| 987 return; | |
| 988 | |
| 989 m_imagesEnabled = enable; | |
| 990 | |
| 991 if (!m_imagesEnabled) | |
| 992 return; | |
| 993 | |
| 994 reloadImagesIfNotDeferred(); | |
| 995 } | |
| 996 | |
| 997 bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const { | |
| 998 return !context().allowImage(m_imagesEnabled, url) || !m_autoLoadImages; | |
| 999 } | |
| 1000 | |
| 1001 void ResourceFetcher::reloadImagesIfNotDeferred() { | |
| 1002 for (Resource* resource : m_documentResources.values()) { | |
| 1003 if (resource->getType() == Resource::Image && resource->stillNeedsLoad() && | |
| 1004 !shouldDeferImageLoad(resource->url())) | |
| 1005 startLoad(resource); | |
| 1006 } | |
| 1007 } | |
| 1008 | |
| 1009 void ResourceFetcher::clearContext() { | |
| 1010 clearPreloads(ResourceFetcher::ClearAllPreloads); | |
| 1011 m_context.clear(); | |
| 1012 } | |
| 1013 | |
| 1014 int ResourceFetcher::requestCount() const { | |
| 1015 return m_loaders.size(); | |
| 1016 } | |
| 1017 | |
| 1018 bool ResourceFetcher::hasPendingRequest() const { | |
| 1019 return m_loaders.size() > 0 || m_nonBlockingLoaders.size() > 0; | |
| 1020 } | |
| 1021 | |
| 1022 void ResourceFetcher::preloadStarted(Resource* resource) { | |
| 1023 if (m_preloads && m_preloads->contains(resource)) | |
| 1024 return; | |
| 1025 resource->increasePreloadCount(); | |
| 1026 | |
| 1027 if (!m_preloads) | |
| 1028 m_preloads = new HeapListHashSet<Member<Resource>>; | |
| 1029 m_preloads->add(resource); | |
| 1030 | |
| 1031 if (m_preloadedURLsForTest) | |
| 1032 m_preloadedURLsForTest->add(resource->url().getString()); | |
| 1033 } | |
| 1034 | |
| 1035 void ResourceFetcher::enableIsPreloadedForTest() { | |
| 1036 if (m_preloadedURLsForTest) | |
| 1037 return; | |
| 1038 m_preloadedURLsForTest = WTF::wrapUnique(new HashSet<String>); | |
| 1039 | |
| 1040 if (m_preloads) { | |
| 1041 for (const auto& resource : *m_preloads) | |
| 1042 m_preloadedURLsForTest->add(resource->url().getString()); | |
| 1043 } | |
| 1044 } | |
| 1045 | |
| 1046 bool ResourceFetcher::isPreloadedForTest(const KURL& url) const { | |
| 1047 DCHECK(m_preloadedURLsForTest); | |
| 1048 return m_preloadedURLsForTest->contains(url.getString()); | |
| 1049 } | |
| 1050 | |
| 1051 void ResourceFetcher::clearPreloads(ClearPreloadsPolicy policy) { | |
| 1052 if (!m_preloads) | |
| 1053 return; | |
| 1054 | |
| 1055 logPreloadStats(policy); | |
| 1056 | |
| 1057 for (const auto& resource : *m_preloads) { | |
| 1058 if (policy == ClearAllPreloads || !resource->isLinkPreload()) { | |
| 1059 resource->decreasePreloadCount(); | |
| 1060 if (resource->getPreloadResult() == Resource::PreloadNotReferenced) | |
| 1061 memoryCache()->remove(resource.get()); | |
| 1062 m_preloads->remove(resource); | |
| 1063 } | |
| 1064 } | |
| 1065 if (!m_preloads->size()) | |
| 1066 m_preloads.clear(); | |
| 1067 } | |
| 1068 | |
| 1069 void ResourceFetcher::warnUnusedPreloads() { | |
| 1070 if (!m_preloads) | |
| 1071 return; | |
| 1072 for (const auto& resource : *m_preloads) { | |
| 1073 if (resource && resource->isLinkPreload() && | |
| 1074 resource->getPreloadResult() == Resource::PreloadNotReferenced) { | |
| 1075 context().addConsoleMessage( | |
| 1076 "The resource " + resource->url().getString() + | |
| 1077 " was preloaded using link preload but not used within a few " | |
| 1078 "seconds from the window's load event. Please make sure it " | |
| 1079 "wasn't preloaded for nothing.", | |
| 1080 FetchContext::LogWarningMessage); | |
| 1081 } | |
| 1082 } | |
| 1083 } | |
| 1084 | |
| 1085 ArchiveResource* ResourceFetcher::createArchive(Resource* resource) { | |
| 1086 // Only the top-frame can load MHTML. | |
| 1087 if (!context().isMainFrame()) | |
| 1088 return nullptr; | |
| 1089 m_archive = MHTMLArchive::create(resource->url(), resource->resourceBuffer()); | |
| 1090 return m_archive ? m_archive->mainResource() : nullptr; | |
| 1091 } | |
| 1092 | |
| 1093 ResourceTimingInfo* ResourceFetcher::getNavigationTimingInfo() { | |
| 1094 return m_navigationTimingInfo.get(); | |
| 1095 } | |
| 1096 | |
| 1097 void ResourceFetcher::handleLoadCompletion(Resource* resource) { | |
| 1098 context().didLoadResource(resource); | |
| 1099 | |
| 1100 resource->reloadIfLoFiOrPlaceholderImage(this, Resource::kReloadIfNeeded); | |
| 1101 } | |
| 1102 | |
| 1103 void ResourceFetcher::handleLoaderFinish(Resource* resource, | |
| 1104 double finishTime, | |
| 1105 LoaderFinishType type) { | |
| 1106 DCHECK(resource); | |
| 1107 | |
| 1108 ResourceLoader* loader = resource->loader(); | |
| 1109 if (type == DidFinishFirstPartInMultipart) { | |
| 1110 // When loading a multipart resource, make the loader non-block when | |
| 1111 // finishing loading the first part. | |
| 1112 moveResourceLoaderToNonBlocking(loader); | |
| 1113 } else { | |
| 1114 removeResourceLoader(loader); | |
| 1115 DCHECK(!m_nonBlockingLoaders.contains(loader)); | |
| 1116 } | |
| 1117 DCHECK(!m_loaders.contains(loader)); | |
| 1118 | |
| 1119 const int64_t encodedDataLength = resource->response().encodedDataLength(); | |
| 1120 | |
| 1121 if (resource->getType() == Resource::MainResource) { | |
| 1122 DCHECK(m_navigationTimingInfo); | |
| 1123 // Store redirect responses that were packed inside the final response. | |
| 1124 addRedirectsToTimingInfo(resource, m_navigationTimingInfo.get()); | |
| 1125 if (resource->response().isHTTP()) { | |
| 1126 populateTimingInfo(m_navigationTimingInfo.get(), resource); | |
| 1127 m_navigationTimingInfo->addFinalTransferSize( | |
| 1128 encodedDataLength == -1 ? 0 : encodedDataLength); | |
| 1129 } | |
| 1130 } | |
| 1131 if (std::unique_ptr<ResourceTimingInfo> info = | |
| 1132 m_resourceTimingInfoMap.take(resource)) { | |
| 1133 // Store redirect responses that were packed inside the final response. | |
| 1134 addRedirectsToTimingInfo(resource, info.get()); | |
| 1135 | |
| 1136 if (resource->response().isHTTP() && | |
| 1137 resource->response().httpStatusCode() < 400) { | |
| 1138 populateTimingInfo(info.get(), resource); | |
| 1139 info->setLoadFinishTime(finishTime); | |
| 1140 // encodedDataLength == -1 means "not available". | |
| 1141 // TODO(ricea): Find cases where it is not available but the | |
| 1142 // PerformanceResourceTiming spec requires it to be available and fix | |
| 1143 // them. | |
| 1144 info->addFinalTransferSize(encodedDataLength == -1 ? 0 | |
| 1145 : encodedDataLength); | |
| 1146 | |
| 1147 if (resource->options().requestInitiatorContext == DocumentContext) | |
| 1148 context().addResourceTiming(*info); | |
| 1149 resource->reportResourceTimingToClients(*info); | |
| 1150 } | |
| 1151 } | |
| 1152 | |
| 1153 context().dispatchDidFinishLoading(resource->identifier(), finishTime, | |
| 1154 encodedDataLength); | |
| 1155 | |
| 1156 if (type == DidFinishLoading) | |
| 1157 resource->finish(finishTime); | |
| 1158 | |
| 1159 handleLoadCompletion(resource); | |
| 1160 } | |
| 1161 | |
| 1162 void ResourceFetcher::handleLoaderError(Resource* resource, | |
| 1163 const ResourceError& error) { | |
| 1164 DCHECK(resource); | |
| 1165 | |
| 1166 removeResourceLoader(resource->loader()); | |
| 1167 | |
| 1168 m_resourceTimingInfoMap.take(resource); | |
| 1169 | |
| 1170 bool isInternalRequest = resource->options().initiatorInfo.name == | |
| 1171 FetchInitiatorTypeNames::internal; | |
| 1172 | |
| 1173 context().dispatchDidFail(resource->identifier(), error, | |
| 1174 resource->response().encodedDataLength(), | |
| 1175 isInternalRequest); | |
| 1176 | |
| 1177 resource->error(error); | |
| 1178 | |
| 1179 handleLoadCompletion(resource); | |
| 1180 } | |
| 1181 | |
| 1182 void ResourceFetcher::moveResourceLoaderToNonBlocking(ResourceLoader* loader) { | |
| 1183 DCHECK(loader); | |
| 1184 // TODO(yoav): Convert CHECK to DCHECK if no crash reports come in. | |
| 1185 CHECK(m_loaders.contains(loader)); | |
| 1186 m_nonBlockingLoaders.add(loader); | |
| 1187 m_loaders.remove(loader); | |
| 1188 } | |
| 1189 | |
| 1190 bool ResourceFetcher::startLoad(Resource* resource) { | |
| 1191 DCHECK(resource); | |
| 1192 DCHECK(resource->stillNeedsLoad()); | |
| 1193 if (!context().shouldLoadNewResource(resource->getType())) { | |
| 1194 memoryCache()->remove(resource); | |
| 1195 return false; | |
| 1196 } | |
| 1197 | |
| 1198 ResourceRequest request(resource->resourceRequest()); | |
| 1199 context().dispatchWillSendRequest(resource->identifier(), request, | |
| 1200 ResourceResponse(), | |
| 1201 resource->options().initiatorInfo); | |
| 1202 | |
| 1203 // TODO(shaochuan): Saving modified ResourceRequest back to |resource|, remove | |
| 1204 // once dispatchWillSendRequest() takes const ResourceRequest. | |
| 1205 // crbug.com/632580 | |
| 1206 resource->setResourceRequest(request); | |
| 1207 | |
| 1208 // Resource requests from suborigins should not be intercepted by the service | |
| 1209 // worker of the physical origin. This has the effect that, for now, | |
| 1210 // suborigins do not work with service workers. See | |
| 1211 // https://w3c.github.io/webappsec-suborigins/. | |
| 1212 SecurityOrigin* sourceOrigin = context().getSecurityOrigin(); | |
| 1213 if (sourceOrigin && sourceOrigin->hasSuborigin()) | |
| 1214 request.setSkipServiceWorker(WebURLRequest::SkipServiceWorker::All); | |
| 1215 | |
| 1216 ResourceLoader* loader = ResourceLoader::create(this, resource); | |
| 1217 if (resource->shouldBlockLoadEvent()) | |
| 1218 m_loaders.add(loader); | |
| 1219 else | |
| 1220 m_nonBlockingLoaders.add(loader); | |
| 1221 | |
| 1222 storePerformanceTimingInitiatorInformation(resource); | |
| 1223 resource->setFetcherSecurityOrigin(sourceOrigin); | |
| 1224 | |
| 1225 loader->activateCacheAwareLoadingIfNeeded(request); | |
| 1226 loader->start(request); | |
| 1227 return true; | |
| 1228 } | |
| 1229 | |
| 1230 void ResourceFetcher::removeResourceLoader(ResourceLoader* loader) { | |
| 1231 DCHECK(loader); | |
| 1232 if (m_loaders.contains(loader)) | |
| 1233 m_loaders.remove(loader); | |
| 1234 else if (m_nonBlockingLoaders.contains(loader)) | |
| 1235 m_nonBlockingLoaders.remove(loader); | |
| 1236 else | |
| 1237 NOTREACHED(); | |
| 1238 } | |
| 1239 | |
| 1240 void ResourceFetcher::stopFetching() { | |
| 1241 HeapVector<Member<ResourceLoader>> loadersToCancel; | |
| 1242 for (const auto& loader : m_nonBlockingLoaders) | |
| 1243 loadersToCancel.push_back(loader); | |
| 1244 for (const auto& loader : m_loaders) | |
| 1245 loadersToCancel.push_back(loader); | |
| 1246 | |
| 1247 for (const auto& loader : loadersToCancel) { | |
| 1248 if (m_loaders.contains(loader) || m_nonBlockingLoaders.contains(loader)) | |
| 1249 loader->cancel(); | |
| 1250 } | |
| 1251 } | |
| 1252 | |
| 1253 bool ResourceFetcher::isFetching() const { | |
| 1254 return !m_loaders.isEmpty(); | |
| 1255 } | |
| 1256 | |
| 1257 void ResourceFetcher::setDefersLoading(bool defers) { | |
| 1258 for (const auto& loader : m_nonBlockingLoaders) | |
| 1259 loader->setDefersLoading(defers); | |
| 1260 for (const auto& loader : m_loaders) | |
| 1261 loader->setDefersLoading(defers); | |
| 1262 } | |
| 1263 | |
| 1264 void ResourceFetcher::updateAllImageResourcePriorities() { | |
| 1265 TRACE_EVENT0( | |
| 1266 "blink", | |
| 1267 "ResourceLoadPriorityOptimizer::updateAllImageResourcePriorities"); | |
| 1268 for (const auto& documentResource : m_documentResources) { | |
| 1269 Resource* resource = documentResource.value.get(); | |
| 1270 if (!resource || !resource->isImage() || !resource->isLoading()) | |
| 1271 continue; | |
| 1272 | |
| 1273 ResourcePriority resourcePriority = resource->priorityFromObservers(); | |
| 1274 ResourceLoadPriority resourceLoadPriority = computeLoadPriority( | |
| 1275 Resource::Image, | |
| 1276 FetchRequest(resource->resourceRequest(), FetchInitiatorInfo()), | |
| 1277 resourcePriority.visibility); | |
| 1278 if (resourceLoadPriority == resource->resourceRequest().priority()) | |
| 1279 continue; | |
| 1280 | |
| 1281 resource->didChangePriority(resourceLoadPriority, | |
| 1282 resourcePriority.intraPriorityValue); | |
| 1283 network_instrumentation::resourcePrioritySet(resource->identifier(), | |
| 1284 resourceLoadPriority); | |
| 1285 context().dispatchDidChangeResourcePriority( | |
| 1286 resource->identifier(), resourceLoadPriority, | |
| 1287 resourcePriority.intraPriorityValue); | |
| 1288 } | |
| 1289 } | |
| 1290 | |
| 1291 void ResourceFetcher::reloadLoFiImages() { | |
| 1292 for (const auto& documentResource : m_documentResources) { | |
| 1293 Resource* resource = documentResource.value.get(); | |
| 1294 if (resource) | |
| 1295 resource->reloadIfLoFiOrPlaceholderImage(this, Resource::kReloadAlways); | |
| 1296 } | |
| 1297 } | |
| 1298 | |
| 1299 void ResourceFetcher::logPreloadStats(ClearPreloadsPolicy policy) { | |
| 1300 if (!m_preloads) | |
| 1301 return; | |
| 1302 unsigned scripts = 0; | |
| 1303 unsigned scriptMisses = 0; | |
| 1304 unsigned stylesheets = 0; | |
| 1305 unsigned stylesheetMisses = 0; | |
| 1306 unsigned images = 0; | |
| 1307 unsigned imageMisses = 0; | |
| 1308 unsigned fonts = 0; | |
| 1309 unsigned fontMisses = 0; | |
| 1310 unsigned medias = 0; | |
| 1311 unsigned mediaMisses = 0; | |
| 1312 unsigned textTracks = 0; | |
| 1313 unsigned textTrackMisses = 0; | |
| 1314 unsigned imports = 0; | |
| 1315 unsigned importMisses = 0; | |
| 1316 unsigned raws = 0; | |
| 1317 unsigned rawMisses = 0; | |
| 1318 for (const auto& resource : *m_preloads) { | |
| 1319 // Do not double count link rel preloads. These do not get cleared if the | |
| 1320 // ClearPreloadsPolicy is only clearing speculative markup preloads. | |
| 1321 if (resource->isLinkPreload() && policy == ClearSpeculativeMarkupPreloads) { | |
| 1322 continue; | |
| 1323 } | |
| 1324 int missCount = | |
| 1325 resource->getPreloadResult() == Resource::PreloadNotReferenced ? 1 : 0; | |
| 1326 switch (resource->getType()) { | |
| 1327 case Resource::Image: | |
| 1328 images++; | |
| 1329 imageMisses += missCount; | |
| 1330 break; | |
| 1331 case Resource::Script: | |
| 1332 scripts++; | |
| 1333 scriptMisses += missCount; | |
| 1334 break; | |
| 1335 case Resource::CSSStyleSheet: | |
| 1336 stylesheets++; | |
| 1337 stylesheetMisses += missCount; | |
| 1338 break; | |
| 1339 case Resource::Font: | |
| 1340 fonts++; | |
| 1341 fontMisses += missCount; | |
| 1342 break; | |
| 1343 case Resource::Media: | |
| 1344 medias++; | |
| 1345 mediaMisses += missCount; | |
| 1346 break; | |
| 1347 case Resource::TextTrack: | |
| 1348 textTracks++; | |
| 1349 textTrackMisses += missCount; | |
| 1350 break; | |
| 1351 case Resource::ImportResource: | |
| 1352 imports++; | |
| 1353 importMisses += missCount; | |
| 1354 break; | |
| 1355 case Resource::Raw: | |
| 1356 raws++; | |
| 1357 rawMisses += missCount; | |
| 1358 break; | |
| 1359 case Resource::Mock: | |
| 1360 // Do not count Resource::Mock because this type is only for testing. | |
| 1361 break; | |
| 1362 default: | |
| 1363 NOTREACHED(); | |
| 1364 } | |
| 1365 } | |
| 1366 DEFINE_STATIC_LOCAL(CustomCountHistogram, imagePreloads, | |
| 1367 ("PreloadScanner.Counts2.Image", 0, 100, 25)); | |
| 1368 DEFINE_STATIC_LOCAL(CustomCountHistogram, imagePreloadMisses, | |
| 1369 ("PreloadScanner.Counts2.Miss.Image", 0, 100, 25)); | |
| 1370 DEFINE_STATIC_LOCAL(CustomCountHistogram, scriptPreloads, | |
| 1371 ("PreloadScanner.Counts2.Script", 0, 100, 25)); | |
| 1372 DEFINE_STATIC_LOCAL(CustomCountHistogram, scriptPreloadMisses, | |
| 1373 ("PreloadScanner.Counts2.Miss.Script", 0, 100, 25)); | |
| 1374 DEFINE_STATIC_LOCAL(CustomCountHistogram, stylesheetPreloads, | |
| 1375 ("PreloadScanner.Counts2.CSSStyleSheet", 0, 100, 25)); | |
| 1376 DEFINE_STATIC_LOCAL( | |
| 1377 CustomCountHistogram, stylesheetPreloadMisses, | |
| 1378 ("PreloadScanner.Counts2.Miss.CSSStyleSheet", 0, 100, 25)); | |
| 1379 DEFINE_STATIC_LOCAL(CustomCountHistogram, fontPreloads, | |
| 1380 ("PreloadScanner.Counts2.Font", 0, 100, 25)); | |
| 1381 DEFINE_STATIC_LOCAL(CustomCountHistogram, fontPreloadMisses, | |
| 1382 ("PreloadScanner.Counts2.Miss.Font", 0, 100, 25)); | |
| 1383 DEFINE_STATIC_LOCAL(CustomCountHistogram, mediaPreloads, | |
| 1384 ("PreloadScanner.Counts2.Media", 0, 100, 25)); | |
| 1385 DEFINE_STATIC_LOCAL(CustomCountHistogram, mediaPreloadMisses, | |
| 1386 ("PreloadScanner.Counts2.Miss.Media", 0, 100, 25)); | |
| 1387 DEFINE_STATIC_LOCAL(CustomCountHistogram, textTrackPreloads, | |
| 1388 ("PreloadScanner.Counts2.TextTrack", 0, 100, 25)); | |
| 1389 DEFINE_STATIC_LOCAL(CustomCountHistogram, textTrackPreloadMisses, | |
| 1390 ("PreloadScanner.Counts2.Miss.TextTrack", 0, 100, 25)); | |
| 1391 DEFINE_STATIC_LOCAL(CustomCountHistogram, importPreloads, | |
| 1392 ("PreloadScanner.Counts2.Import", 0, 100, 25)); | |
| 1393 DEFINE_STATIC_LOCAL(CustomCountHistogram, importPreloadMisses, | |
| 1394 ("PreloadScanner.Counts2.Miss.Import", 0, 100, 25)); | |
| 1395 DEFINE_STATIC_LOCAL(CustomCountHistogram, rawPreloads, | |
| 1396 ("PreloadScanner.Counts2.Raw", 0, 100, 25)); | |
| 1397 DEFINE_STATIC_LOCAL(CustomCountHistogram, rawPreloadMisses, | |
| 1398 ("PreloadScanner.Counts2.Miss.Raw", 0, 100, 25)); | |
| 1399 if (images) | |
| 1400 imagePreloads.count(images); | |
| 1401 if (imageMisses) | |
| 1402 imagePreloadMisses.count(imageMisses); | |
| 1403 if (scripts) | |
| 1404 scriptPreloads.count(scripts); | |
| 1405 if (scriptMisses) | |
| 1406 scriptPreloadMisses.count(scriptMisses); | |
| 1407 if (stylesheets) | |
| 1408 stylesheetPreloads.count(stylesheets); | |
| 1409 if (stylesheetMisses) | |
| 1410 stylesheetPreloadMisses.count(stylesheetMisses); | |
| 1411 if (fonts) | |
| 1412 fontPreloads.count(fonts); | |
| 1413 if (fontMisses) | |
| 1414 fontPreloadMisses.count(fontMisses); | |
| 1415 if (medias) | |
| 1416 mediaPreloads.count(medias); | |
| 1417 if (mediaMisses) | |
| 1418 mediaPreloadMisses.count(mediaMisses); | |
| 1419 if (textTracks) | |
| 1420 textTrackPreloads.count(textTracks); | |
| 1421 if (textTrackMisses) | |
| 1422 textTrackPreloadMisses.count(textTrackMisses); | |
| 1423 if (imports) | |
| 1424 importPreloads.count(imports); | |
| 1425 if (importMisses) | |
| 1426 importPreloadMisses.count(importMisses); | |
| 1427 if (raws) | |
| 1428 rawPreloads.count(raws); | |
| 1429 if (rawMisses) | |
| 1430 rawPreloadMisses.count(rawMisses); | |
| 1431 } | |
| 1432 | |
| 1433 const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions() { | |
| 1434 DEFINE_STATIC_LOCAL( | |
| 1435 ResourceLoaderOptions, options, | |
| 1436 (BufferData, AllowStoredCredentials, ClientRequestedCredentials, | |
| 1437 CheckContentSecurityPolicy, DocumentContext)); | |
| 1438 return options; | |
| 1439 } | |
| 1440 | |
| 1441 String ResourceFetcher::getCacheIdentifier() const { | |
| 1442 if (context().isControlledByServiceWorker()) | |
| 1443 return String::number(context().serviceWorkerID()); | |
| 1444 return MemoryCache::defaultCacheIdentifier(); | |
| 1445 } | |
| 1446 | |
| 1447 void ResourceFetcher::emulateLoadStartedForInspector( | |
| 1448 Resource* resource, | |
| 1449 const KURL& url, | |
| 1450 WebURLRequest::RequestContext requestContext, | |
| 1451 const AtomicString& initiatorName) { | |
| 1452 if (cachedResource(url)) | |
| 1453 return; | |
| 1454 ResourceRequest resourceRequest(url); | |
| 1455 resourceRequest.setRequestContext(requestContext); | |
| 1456 FetchRequest request(resourceRequest, initiatorName, resource->options()); | |
| 1457 context().canRequest(resource->getType(), resource->lastResourceRequest(), | |
| 1458 resource->lastResourceRequest().url(), request.options(), | |
| 1459 false, request.getOriginRestriction()); | |
| 1460 requestLoadStarted(resource->identifier(), resource, request, | |
| 1461 ResourceLoadingFromCache); | |
| 1462 } | |
| 1463 | |
| 1464 ResourceFetcher::DeadResourceStatsRecorder::DeadResourceStatsRecorder() | |
| 1465 : m_useCount(0), m_revalidateCount(0), m_loadCount(0) {} | |
| 1466 | |
| 1467 ResourceFetcher::DeadResourceStatsRecorder::~DeadResourceStatsRecorder() { | |
| 1468 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
| 1469 CustomCountHistogram, hitCountHistogram, | |
| 1470 new CustomCountHistogram("WebCore.ResourceFetcher.HitCount", 0, 1000, | |
| 1471 50)); | |
| 1472 hitCountHistogram.count(m_useCount); | |
| 1473 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
| 1474 CustomCountHistogram, revalidateCountHistogram, | |
| 1475 new CustomCountHistogram("WebCore.ResourceFetcher.RevalidateCount", 0, | |
| 1476 1000, 50)); | |
| 1477 revalidateCountHistogram.count(m_revalidateCount); | |
| 1478 DEFINE_THREAD_SAFE_STATIC_LOCAL( | |
| 1479 CustomCountHistogram, loadCountHistogram, | |
| 1480 new CustomCountHistogram("WebCore.ResourceFetcher.LoadCount", 0, 1000, | |
| 1481 50)); | |
| 1482 loadCountHistogram.count(m_loadCount); | |
| 1483 } | |
| 1484 | |
| 1485 void ResourceFetcher::DeadResourceStatsRecorder::update( | |
| 1486 RevalidationPolicy policy) { | |
| 1487 switch (policy) { | |
| 1488 case Reload: | |
| 1489 case Load: | |
| 1490 ++m_loadCount; | |
| 1491 return; | |
| 1492 case Revalidate: | |
| 1493 ++m_revalidateCount; | |
| 1494 return; | |
| 1495 case Use: | |
| 1496 ++m_useCount; | |
| 1497 return; | |
| 1498 } | |
| 1499 } | |
| 1500 | |
| 1501 DEFINE_TRACE(ResourceFetcher) { | |
| 1502 visitor->trace(m_context); | |
| 1503 visitor->trace(m_archive); | |
| 1504 visitor->trace(m_loaders); | |
| 1505 visitor->trace(m_nonBlockingLoaders); | |
| 1506 visitor->trace(m_documentResources); | |
| 1507 visitor->trace(m_preloads); | |
| 1508 visitor->trace(m_resourceTimingInfoMap); | |
| 1509 } | |
| 1510 | |
| 1511 } // namespace blink | |
| OLD | NEW |