Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp

Issue 2584423002: Loading: move core/fetch to platform/loader/fetch (Closed)
Patch Set: rebase Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698