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

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

Issue 2584423002: Loading: move core/fetch to platform/loader/fetch (Closed)
Patch Set: another try Created 3 years, 10 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) 2006 Samuel Weinig (sam.weinig@gmail.com)
6 Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
7 rights reserved.
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
25 #include "core/fetch/Resource.h"
26
27 #include "core/fetch/CachedMetadata.h"
28 #include "core/fetch/CrossOriginAccessControl.h"
29 #include "core/fetch/FetchInitiatorTypeNames.h"
30 #include "core/fetch/FetchRequest.h"
31 #include "core/fetch/IntegrityMetadata.h"
32 #include "core/fetch/MemoryCache.h"
33 #include "core/fetch/ResourceClient.h"
34 #include "core/fetch/ResourceClientWalker.h"
35 #include "core/fetch/ResourceLoader.h"
36 #include "platform/Histogram.h"
37 #include "platform/InstanceCounters.h"
38 #include "platform/RuntimeEnabledFeatures.h"
39 #include "platform/SharedBuffer.h"
40 #include "platform/WebTaskRunner.h"
41 #include "platform/instrumentation/tracing/TraceEvent.h"
42 #include "platform/network/HTTPParsers.h"
43 #include "platform/weborigin/KURL.h"
44 #include "public/platform/Platform.h"
45 #include "public/platform/WebCachePolicy.h"
46 #include "public/platform/WebScheduler.h"
47 #include "public/platform/WebSecurityOrigin.h"
48 #include "wtf/CurrentTime.h"
49 #include "wtf/MathExtras.h"
50 #include "wtf/StdLibExtras.h"
51 #include "wtf/Vector.h"
52 #include "wtf/text/CString.h"
53 #include "wtf/text/StringBuilder.h"
54 #include <algorithm>
55 #include <cassert>
56 #include <memory>
57 #include <stdint.h>
58
59 namespace blink {
60
61 // These response headers are not copied from a revalidated response to the
62 // cached response headers. For compatibility, this list is based on Chromium's
63 // net/http/http_response_headers.cc.
64 const char* const headersToIgnoreAfterRevalidation[] = {
65 "allow",
66 "connection",
67 "etag",
68 "expires",
69 "keep-alive",
70 "last-modified",
71 "proxy-authenticate",
72 "proxy-connection",
73 "trailer",
74 "transfer-encoding",
75 "upgrade",
76 "www-authenticate",
77 "x-frame-options",
78 "x-xss-protection",
79 };
80
81 // Some header prefixes mean "Don't copy this header from a 304 response.".
82 // Rather than listing all the relevant headers, we can consolidate them into
83 // this list, also grabbed from Chromium's net/http/http_response_headers.cc.
84 const char* const headerPrefixesToIgnoreAfterRevalidation[] = {
85 "content-", "x-content-", "x-webkit-"};
86
87 static inline bool shouldUpdateHeaderAfterRevalidation(
88 const AtomicString& header) {
89 for (size_t i = 0; i < WTF_ARRAY_LENGTH(headersToIgnoreAfterRevalidation);
90 i++) {
91 if (equalIgnoringCase(header, headersToIgnoreAfterRevalidation[i]))
92 return false;
93 }
94 for (size_t i = 0;
95 i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) {
96 if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i],
97 TextCaseASCIIInsensitive))
98 return false;
99 }
100 return true;
101 }
102
103 class Resource::CachedMetadataHandlerImpl : public CachedMetadataHandler {
104 public:
105 static Resource::CachedMetadataHandlerImpl* create(Resource* resource) {
106 return new CachedMetadataHandlerImpl(resource);
107 }
108 ~CachedMetadataHandlerImpl() override {}
109 DECLARE_VIRTUAL_TRACE();
110 void setCachedMetadata(uint32_t, const char*, size_t, CacheType) override;
111 void clearCachedMetadata(CacheType) override;
112 PassRefPtr<CachedMetadata> cachedMetadata(uint32_t) const override;
113 String encoding() const override;
114 // Sets the serialized metadata retrieved from the platform's cache.
115 void setSerializedCachedMetadata(const char*, size_t);
116
117 protected:
118 explicit CachedMetadataHandlerImpl(Resource*);
119 virtual void sendToPlatform();
120 const ResourceResponse& response() const { return m_resource->response(); }
121
122 RefPtr<CachedMetadata> m_cachedMetadata;
123
124 private:
125 Member<Resource> m_resource;
126 };
127
128 Resource::CachedMetadataHandlerImpl::CachedMetadataHandlerImpl(
129 Resource* resource)
130 : m_resource(resource) {}
131
132 DEFINE_TRACE(Resource::CachedMetadataHandlerImpl) {
133 visitor->trace(m_resource);
134 CachedMetadataHandler::trace(visitor);
135 }
136
137 void Resource::CachedMetadataHandlerImpl::setCachedMetadata(
138 uint32_t dataTypeID,
139 const char* data,
140 size_t size,
141 CachedMetadataHandler::CacheType cacheType) {
142 // Currently, only one type of cached metadata per resource is supported. If
143 // the need arises for multiple types of metadata per resource this could be
144 // enhanced to store types of metadata in a map.
145 DCHECK(!m_cachedMetadata);
146 m_cachedMetadata = CachedMetadata::create(dataTypeID, data, size);
147 if (cacheType == CachedMetadataHandler::SendToPlatform)
148 sendToPlatform();
149 }
150
151 void Resource::CachedMetadataHandlerImpl::clearCachedMetadata(
152 CachedMetadataHandler::CacheType cacheType) {
153 m_cachedMetadata.clear();
154 if (cacheType == CachedMetadataHandler::SendToPlatform)
155 sendToPlatform();
156 }
157
158 PassRefPtr<CachedMetadata> Resource::CachedMetadataHandlerImpl::cachedMetadata(
159 uint32_t dataTypeID) const {
160 if (!m_cachedMetadata || m_cachedMetadata->dataTypeID() != dataTypeID)
161 return nullptr;
162 return m_cachedMetadata.get();
163 }
164
165 String Resource::CachedMetadataHandlerImpl::encoding() const {
166 return m_resource->encoding();
167 }
168
169 void Resource::CachedMetadataHandlerImpl::setSerializedCachedMetadata(
170 const char* data,
171 size_t size) {
172 // We only expect to receive cached metadata from the platform once. If this
173 // triggers, it indicates an efficiency problem which is most likely
174 // unexpected in code designed to improve performance.
175 DCHECK(!m_cachedMetadata);
176 m_cachedMetadata = CachedMetadata::createFromSerializedData(data, size);
177 }
178
179 void Resource::CachedMetadataHandlerImpl::sendToPlatform() {
180 if (m_cachedMetadata) {
181 const Vector<char>& serializedData = m_cachedMetadata->serializedData();
182 Platform::current()->cacheMetadata(
183 response().url(), response().responseTime(), serializedData.data(),
184 serializedData.size());
185 } else {
186 Platform::current()->cacheMetadata(response().url(),
187 response().responseTime(), nullptr, 0);
188 }
189 }
190
191 class Resource::ServiceWorkerResponseCachedMetadataHandler
192 : public Resource::CachedMetadataHandlerImpl {
193 public:
194 static Resource::CachedMetadataHandlerImpl* create(
195 Resource* resource,
196 SecurityOrigin* securityOrigin) {
197 return new ServiceWorkerResponseCachedMetadataHandler(resource,
198 securityOrigin);
199 }
200 ~ServiceWorkerResponseCachedMetadataHandler() override {}
201 DECLARE_VIRTUAL_TRACE();
202
203 protected:
204 void sendToPlatform() override;
205
206 private:
207 explicit ServiceWorkerResponseCachedMetadataHandler(Resource*,
208 SecurityOrigin*);
209 String m_cacheStorageCacheName;
210 RefPtr<SecurityOrigin> m_securityOrigin;
211 };
212
213 Resource::ServiceWorkerResponseCachedMetadataHandler::
214 ServiceWorkerResponseCachedMetadataHandler(Resource* resource,
215 SecurityOrigin* securityOrigin)
216 : CachedMetadataHandlerImpl(resource), m_securityOrigin(securityOrigin) {}
217
218 DEFINE_TRACE(Resource::ServiceWorkerResponseCachedMetadataHandler) {
219 CachedMetadataHandlerImpl::trace(visitor);
220 }
221
222 void Resource::ServiceWorkerResponseCachedMetadataHandler::sendToPlatform() {
223 // We don't support sending the metadata to the platform when the response was
224 // directly fetched via a ServiceWorker (eg:
225 // FetchEvent.respondWith(fetch(FetchEvent.request))) to prevent an attacker's
226 // Service Worker from poisoning the metadata cache of HTTPCache.
227 if (response().cacheStorageCacheName().isNull())
228 return;
229
230 if (m_cachedMetadata) {
231 const Vector<char>& serializedData = m_cachedMetadata->serializedData();
232 Platform::current()->cacheMetadataInCacheStorage(
233 response().url(), response().responseTime(), serializedData.data(),
234 serializedData.size(), WebSecurityOrigin(m_securityOrigin),
235 response().cacheStorageCacheName());
236 } else {
237 Platform::current()->cacheMetadataInCacheStorage(
238 response().url(), response().responseTime(), nullptr, 0,
239 WebSecurityOrigin(m_securityOrigin),
240 response().cacheStorageCacheName());
241 }
242 }
243
244 // This class cannot be on-heap because the first callbackHandler() call
245 // instantiates the singleton object while we can call it in the
246 // pre-finalization step.
247 class Resource::ResourceCallback final {
248 public:
249 static ResourceCallback& callbackHandler();
250 void schedule(Resource*);
251 void cancel(Resource*);
252 bool isScheduled(Resource*) const;
253
254 private:
255 ResourceCallback();
256
257 void runTask();
258 TaskHandle m_taskHandle;
259 HashSet<Persistent<Resource>> m_resourcesWithPendingClients;
260 };
261
262 Resource::ResourceCallback& Resource::ResourceCallback::callbackHandler() {
263 DEFINE_STATIC_LOCAL(ResourceCallback, callbackHandler, ());
264 return callbackHandler;
265 }
266
267 Resource::ResourceCallback::ResourceCallback() {}
268
269 void Resource::ResourceCallback::schedule(Resource* resource) {
270 if (!m_taskHandle.isActive()) {
271 // WTF::unretained(this) is safe because a posted task is canceled when
272 // |m_taskHandle| is destroyed on the dtor of this ResourceCallback.
273 m_taskHandle =
274 Platform::current()
275 ->currentThread()
276 ->scheduler()
277 ->loadingTaskRunner()
278 ->postCancellableTask(
279 BLINK_FROM_HERE,
280 WTF::bind(&ResourceCallback::runTask, WTF::unretained(this)));
281 }
282 m_resourcesWithPendingClients.add(resource);
283 }
284
285 void Resource::ResourceCallback::cancel(Resource* resource) {
286 m_resourcesWithPendingClients.remove(resource);
287 if (m_taskHandle.isActive() && m_resourcesWithPendingClients.isEmpty())
288 m_taskHandle.cancel();
289 }
290
291 bool Resource::ResourceCallback::isScheduled(Resource* resource) const {
292 return m_resourcesWithPendingClients.contains(resource);
293 }
294
295 void Resource::ResourceCallback::runTask() {
296 HeapVector<Member<Resource>> resources;
297 for (const Member<Resource>& resource : m_resourcesWithPendingClients)
298 resources.push_back(resource.get());
299 m_resourcesWithPendingClients.clear();
300
301 for (const auto& resource : resources)
302 resource->finishPendingClients();
303 }
304
305 constexpr Resource::Status Resource::NotStarted;
306 constexpr Resource::Status Resource::Pending;
307 constexpr Resource::Status Resource::Cached;
308 constexpr Resource::Status Resource::LoadError;
309 constexpr Resource::Status Resource::DecodeError;
310
311 Resource::Resource(const ResourceRequest& request,
312 Type type,
313 const ResourceLoaderOptions& options)
314 : m_loadFinishTime(0),
315 m_identifier(0),
316 m_encodedSize(0),
317 m_encodedSizeMemoryUsage(0),
318 m_decodedSize(0),
319 m_overheadSize(calculateOverheadSize()),
320 m_preloadCount(0),
321 m_preloadDiscoveryTime(0.0),
322 m_cacheIdentifier(MemoryCache::defaultCacheIdentifier()),
323 m_preloadResult(PreloadNotReferenced),
324 m_type(type),
325 m_status(NotStarted),
326 m_needsSynchronousCacheHit(false),
327 m_linkPreload(false),
328 m_isRevalidating(false),
329 m_isAlive(false),
330 m_isAddRemoveClientProhibited(false),
331 m_integrityDisposition(ResourceIntegrityDisposition::NotChecked),
332 m_options(options),
333 m_responseTimestamp(currentTime()),
334 m_cancelTimer(Platform::current()->mainThread()->getWebTaskRunner(),
335 this,
336 &Resource::cancelTimerFired),
337 m_resourceRequest(request) {
338 InstanceCounters::incrementCounter(InstanceCounters::ResourceCounter);
339
340 // Currently we support the metadata caching only for HTTP family.
341 if (resourceRequest().url().protocolIsInHTTPFamily())
342 m_cacheHandler = CachedMetadataHandlerImpl::create(this);
343 MemoryCoordinator::instance().registerClient(this);
344 }
345
346 Resource::~Resource() {
347 InstanceCounters::decrementCounter(InstanceCounters::ResourceCounter);
348 }
349
350 DEFINE_TRACE(Resource) {
351 visitor->trace(m_loader);
352 visitor->trace(m_cacheHandler);
353 visitor->trace(m_clients);
354 visitor->trace(m_clientsAwaitingCallback);
355 visitor->trace(m_finishedClients);
356 MemoryCoordinatorClient::trace(visitor);
357 }
358
359 void Resource::setLoader(ResourceLoader* loader) {
360 CHECK(!m_loader);
361 DCHECK(stillNeedsLoad());
362 m_loader = loader;
363 m_status = Pending;
364 }
365
366 void Resource::checkNotify() {
367 if (isLoading())
368 return;
369
370 ResourceClientWalker<ResourceClient> w(m_clients);
371 while (ResourceClient* c = w.next()) {
372 markClientFinished(c);
373 c->notifyFinished(this);
374 }
375 }
376
377 void Resource::markClientFinished(ResourceClient* client) {
378 if (m_clients.contains(client)) {
379 m_finishedClients.add(client);
380 m_clients.remove(client);
381 }
382 }
383
384 void Resource::appendData(const char* data, size_t length) {
385 TRACE_EVENT0("blink", "Resource::appendData");
386 DCHECK(!m_isRevalidating);
387 DCHECK(!errorOccurred());
388 if (m_options.dataBufferingPolicy == DoNotBufferData)
389 return;
390 if (m_data)
391 m_data->append(data, length);
392 else
393 m_data = SharedBuffer::create(data, length);
394 setEncodedSize(m_data->size());
395 }
396
397 void Resource::setResourceBuffer(PassRefPtr<SharedBuffer> resourceBuffer) {
398 DCHECK(!m_isRevalidating);
399 DCHECK(!errorOccurred());
400 DCHECK_EQ(m_options.dataBufferingPolicy, BufferData);
401 m_data = resourceBuffer;
402 setEncodedSize(m_data->size());
403 }
404
405 void Resource::clearData() {
406 m_data.clear();
407 m_encodedSizeMemoryUsage = 0;
408 }
409
410 void Resource::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy) {
411 m_options.dataBufferingPolicy = dataBufferingPolicy;
412 clearData();
413 setEncodedSize(0);
414 }
415
416 void Resource::error(const ResourceError& error) {
417 DCHECK(!error.isNull());
418 m_error = error;
419 m_isRevalidating = false;
420
421 if (m_error.isCancellation() || !isPreloaded())
422 memoryCache()->remove(this);
423
424 if (!errorOccurred())
425 setStatus(LoadError);
426 DCHECK(errorOccurred());
427 clearData();
428 m_loader = nullptr;
429 checkNotify();
430 }
431
432 void Resource::finish(double loadFinishTime) {
433 DCHECK(!m_isRevalidating);
434 m_loadFinishTime = loadFinishTime;
435 if (!errorOccurred())
436 m_status = Cached;
437 m_loader = nullptr;
438 checkNotify();
439 }
440
441 AtomicString Resource::httpContentType() const {
442 return extractMIMETypeFromMediaType(
443 response().httpHeaderField(HTTPNames::Content_Type).lower());
444 }
445
446 bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin) const {
447 StoredCredentials storedCredentials =
448 lastResourceRequest().allowStoredCredentials()
449 ? AllowStoredCredentials
450 : DoNotAllowStoredCredentials;
451 CrossOriginAccessControl::AccessStatus status =
452 CrossOriginAccessControl::checkAccess(response(), storedCredentials,
453 securityOrigin);
454
455 return status == CrossOriginAccessControl::kAccessAllowed;
456 }
457
458 bool Resource::isEligibleForIntegrityCheck(
459 SecurityOrigin* securityOrigin) const {
460 return securityOrigin->canRequest(resourceRequest().url()) ||
461 passesAccessControlCheck(securityOrigin);
462 }
463
464 void Resource::setIntegrityDisposition(
465 ResourceIntegrityDisposition disposition) {
466 DCHECK_NE(disposition, ResourceIntegrityDisposition::NotChecked);
467 DCHECK(m_type == Resource::Script || m_type == Resource::CSSStyleSheet);
468 m_integrityDisposition = disposition;
469 }
470
471 bool Resource::mustRefetchDueToIntegrityMetadata(
472 const FetchRequest& request) const {
473 if (request.integrityMetadata().isEmpty())
474 return false;
475
476 return !IntegrityMetadata::setsEqual(m_integrityMetadata,
477 request.integrityMetadata());
478 }
479
480 static double currentAge(const ResourceResponse& response,
481 double responseTimestamp) {
482 // RFC2616 13.2.3
483 // No compensation for latency as that is not terribly important in practice
484 double dateValue = response.date();
485 double apparentAge = std::isfinite(dateValue)
486 ? std::max(0., responseTimestamp - dateValue)
487 : 0;
488 double ageValue = response.age();
489 double correctedReceivedAge =
490 std::isfinite(ageValue) ? std::max(apparentAge, ageValue) : apparentAge;
491 double residentTime = currentTime() - responseTimestamp;
492 return correctedReceivedAge + residentTime;
493 }
494
495 double Resource::currentAge() const {
496 return blink::currentAge(response(), m_responseTimestamp);
497 }
498
499 static double freshnessLifetime(const ResourceResponse& response,
500 double responseTimestamp) {
501 #if !OS(ANDROID)
502 // On desktop, local files should be reloaded in case they change.
503 if (response.url().isLocalFile())
504 return 0;
505 #endif
506
507 // Cache other non-http / non-filesystem resources liberally.
508 if (!response.url().protocolIsInHTTPFamily() &&
509 !response.url().protocolIs("filesystem"))
510 return std::numeric_limits<double>::max();
511
512 // RFC2616 13.2.4
513 double maxAgeValue = response.cacheControlMaxAge();
514 if (std::isfinite(maxAgeValue))
515 return maxAgeValue;
516 double expiresValue = response.expires();
517 double dateValue = response.date();
518 double creationTime =
519 std::isfinite(dateValue) ? dateValue : responseTimestamp;
520 if (std::isfinite(expiresValue))
521 return expiresValue - creationTime;
522 double lastModifiedValue = response.lastModified();
523 if (std::isfinite(lastModifiedValue))
524 return (creationTime - lastModifiedValue) * 0.1;
525 // If no cache headers are present, the specification leaves the decision to
526 // the UA. Other browsers seem to opt for 0.
527 return 0;
528 }
529
530 double Resource::freshnessLifetime() const {
531 return blink::freshnessLifetime(response(), m_responseTimestamp);
532 }
533
534 double Resource::stalenessLifetime() const {
535 return response().cacheControlStaleWhileRevalidate();
536 }
537
538 static bool canUseResponse(const ResourceResponse& response,
539 double responseTimestamp) {
540 if (response.isNull())
541 return false;
542
543 // FIXME: Why isn't must-revalidate considered a reason we can't use the
544 // response?
545 if (response.cacheControlContainsNoCache() ||
546 response.cacheControlContainsNoStore())
547 return false;
548
549 if (response.httpStatusCode() == 303) {
550 // Must not be cached.
551 return false;
552 }
553
554 if (response.httpStatusCode() == 302 || response.httpStatusCode() == 307) {
555 // Default to not cacheable unless explicitly allowed.
556 bool hasMaxAge = std::isfinite(response.cacheControlMaxAge());
557 bool hasExpires = std::isfinite(response.expires());
558 // TODO: consider catching Cache-Control "private" and "public" here.
559 if (!hasMaxAge && !hasExpires)
560 return false;
561 }
562
563 return currentAge(response, responseTimestamp) <=
564 freshnessLifetime(response, responseTimestamp);
565 }
566
567 const ResourceRequest& Resource::lastResourceRequest() const {
568 if (!m_redirectChain.size())
569 return resourceRequest();
570 return m_redirectChain.back().m_request;
571 }
572
573 void Resource::setRevalidatingRequest(const ResourceRequest& request) {
574 SECURITY_CHECK(m_redirectChain.isEmpty());
575 DCHECK(!request.isNull());
576 CHECK(!m_isRevalidationStartForbidden);
577 m_isRevalidating = true;
578 m_resourceRequest = request;
579 m_status = NotStarted;
580 }
581
582 bool Resource::willFollowRedirect(const ResourceRequest& newRequest,
583 const ResourceResponse& redirectResponse) {
584 if (m_isRevalidating)
585 revalidationFailed();
586 m_redirectChain.push_back(RedirectPair(newRequest, redirectResponse));
587 return true;
588 }
589
590 void Resource::setResponse(const ResourceResponse& response) {
591 m_response = response;
592 if (this->response().wasFetchedViaServiceWorker()) {
593 m_cacheHandler = ServiceWorkerResponseCachedMetadataHandler::create(
594 this, m_fetcherSecurityOrigin.get());
595 }
596 }
597
598 void Resource::responseReceived(const ResourceResponse& response,
599 std::unique_ptr<WebDataConsumerHandle>) {
600 m_responseTimestamp = currentTime();
601 if (m_preloadDiscoveryTime) {
602 int timeSinceDiscovery = static_cast<int>(
603 1000 * (monotonicallyIncreasingTime() - m_preloadDiscoveryTime));
604 DEFINE_STATIC_LOCAL(CustomCountHistogram,
605 preloadDiscoveryToFirstByteHistogram,
606 ("PreloadScanner.TTFB", 0, 10000, 50));
607 preloadDiscoveryToFirstByteHistogram.count(timeSinceDiscovery);
608 }
609
610 if (m_isRevalidating) {
611 if (response.httpStatusCode() == 304) {
612 revalidationSucceeded(response);
613 return;
614 }
615 revalidationFailed();
616 }
617 setResponse(response);
618 String encoding = response.textEncodingName();
619 if (!encoding.isNull())
620 setEncoding(encoding);
621 }
622
623 void Resource::setSerializedCachedMetadata(const char* data, size_t size) {
624 DCHECK(!m_isRevalidating);
625 DCHECK(!response().isNull());
626 if (m_cacheHandler)
627 m_cacheHandler->setSerializedCachedMetadata(data, size);
628 }
629
630 CachedMetadataHandler* Resource::cacheHandler() {
631 return m_cacheHandler.get();
632 }
633
634 String Resource::reasonNotDeletable() const {
635 StringBuilder builder;
636 if (hasClientsOrObservers()) {
637 builder.append("hasClients(");
638 builder.appendNumber(m_clients.size());
639 if (!m_clientsAwaitingCallback.isEmpty()) {
640 builder.append(", AwaitingCallback=");
641 builder.appendNumber(m_clientsAwaitingCallback.size());
642 }
643 if (!m_finishedClients.isEmpty()) {
644 builder.append(", Finished=");
645 builder.appendNumber(m_finishedClients.size());
646 }
647 builder.append(')');
648 }
649 if (m_loader) {
650 if (!builder.isEmpty())
651 builder.append(' ');
652 builder.append("m_loader");
653 }
654 if (m_preloadCount) {
655 if (!builder.isEmpty())
656 builder.append(' ');
657 builder.append("m_preloadCount(");
658 builder.appendNumber(m_preloadCount);
659 builder.append(')');
660 }
661 if (memoryCache()->contains(this)) {
662 if (!builder.isEmpty())
663 builder.append(' ');
664 builder.append("in_memory_cache");
665 }
666 return builder.toString();
667 }
668
669 void Resource::didAddClient(ResourceClient* c) {
670 if (isLoaded()) {
671 c->notifyFinished(this);
672 if (m_clients.contains(c)) {
673 m_finishedClients.add(c);
674 m_clients.remove(c);
675 }
676 }
677 }
678
679 static bool typeNeedsSynchronousCacheHit(Resource::Type type) {
680 // Some resources types default to return data synchronously. For most of
681 // these, it's because there are layout tests that expect data to return
682 // synchronously in case of cache hit. In the case of fonts, there was a
683 // performance regression.
684 // FIXME: Get to the point where we don't need to special-case sync/async
685 // behavior for different resource types.
686 if (type == Resource::Image)
687 return true;
688 if (type == Resource::CSSStyleSheet)
689 return true;
690 if (type == Resource::Script)
691 return true;
692 if (type == Resource::Font)
693 return true;
694 return false;
695 }
696
697 void Resource::willAddClientOrObserver(PreloadReferencePolicy policy) {
698 if (policy == MarkAsReferenced && m_preloadResult == PreloadNotReferenced) {
699 if (isLoaded())
700 m_preloadResult = PreloadReferencedWhileComplete;
701 else if (isLoading())
702 m_preloadResult = PreloadReferencedWhileLoading;
703 else
704 m_preloadResult = PreloadReferenced;
705
706 if (m_preloadDiscoveryTime) {
707 int timeSinceDiscovery = static_cast<int>(
708 1000 * (monotonicallyIncreasingTime() - m_preloadDiscoveryTime));
709 DEFINE_STATIC_LOCAL(CustomCountHistogram, preloadDiscoveryHistogram,
710 ("PreloadScanner.ReferenceTime", 0, 10000, 50));
711 preloadDiscoveryHistogram.count(timeSinceDiscovery);
712 }
713 }
714 if (!hasClientsOrObservers()) {
715 m_isAlive = true;
716 }
717 }
718
719 void Resource::addClient(ResourceClient* client,
720 PreloadReferencePolicy policy) {
721 CHECK(!m_isAddRemoveClientProhibited);
722
723 willAddClientOrObserver(policy);
724
725 if (m_isRevalidating) {
726 m_clients.add(client);
727 return;
728 }
729
730 // If an error has occurred or we have existing data to send to the new client
731 // and the resource type supprts it, send it asynchronously.
732 if ((errorOccurred() || !response().isNull()) &&
733 !typeNeedsSynchronousCacheHit(getType()) && !m_needsSynchronousCacheHit) {
734 m_clientsAwaitingCallback.add(client);
735 ResourceCallback::callbackHandler().schedule(this);
736 return;
737 }
738
739 m_clients.add(client);
740 didAddClient(client);
741 return;
742 }
743
744 void Resource::removeClient(ResourceClient* client) {
745 CHECK(!m_isAddRemoveClientProhibited);
746
747 // This code may be called in a pre-finalizer, where weak members in the
748 // HashCountedSet are already swept out.
749
750 if (m_finishedClients.contains(client))
751 m_finishedClients.remove(client);
752 else if (m_clientsAwaitingCallback.contains(client))
753 m_clientsAwaitingCallback.remove(client);
754 else
755 m_clients.remove(client);
756
757 if (m_clientsAwaitingCallback.isEmpty())
758 ResourceCallback::callbackHandler().cancel(this);
759
760 didRemoveClientOrObserver();
761 }
762
763 void Resource::didRemoveClientOrObserver() {
764 if (!hasClientsOrObservers() && m_isAlive) {
765 m_isAlive = false;
766 allClientsAndObserversRemoved();
767
768 // RFC2616 14.9.2:
769 // "no-store: ... MUST make a best-effort attempt to remove the information
770 // from volatile storage as promptly as possible"
771 // "... History buffers MAY store such responses as part of their normal
772 // operation."
773 // We allow non-secure content to be reused in history, but we do not allow
774 // secure content to be reused.
775 if (hasCacheControlNoStoreHeader() && url().protocolIs("https"))
776 memoryCache()->remove(this);
777 }
778 }
779
780 void Resource::allClientsAndObserversRemoved() {
781 if (!m_loader)
782 return;
783 if (!m_cancelTimer.isActive())
784 m_cancelTimer.startOneShot(0, BLINK_FROM_HERE);
785 }
786
787 void Resource::cancelTimerFired(TimerBase* timer) {
788 DCHECK_EQ(timer, &m_cancelTimer);
789 if (!hasClientsOrObservers() && m_loader)
790 m_loader->cancel();
791 }
792
793 void Resource::setDecodedSize(size_t decodedSize) {
794 if (decodedSize == m_decodedSize)
795 return;
796 size_t oldSize = size();
797 m_decodedSize = decodedSize;
798 memoryCache()->update(this, oldSize, size());
799 }
800
801 void Resource::setEncodedSize(size_t encodedSize) {
802 if (encodedSize == m_encodedSize && encodedSize == m_encodedSizeMemoryUsage)
803 return;
804 size_t oldSize = size();
805 m_encodedSize = encodedSize;
806 m_encodedSizeMemoryUsage = encodedSize;
807 memoryCache()->update(this, oldSize, size());
808 }
809
810 void Resource::finishPendingClients() {
811 // We're going to notify clients one by one. It is simple if the client does
812 // nothing. However there are a couple other things that can happen.
813 //
814 // 1. Clients can be added during the loop. Make sure they are not processed.
815 // 2. Clients can be removed during the loop. Make sure they are always
816 // available to be removed. Also don't call removed clients or add them
817 // back.
818 //
819 // Handle case (1) by saving a list of clients to notify. A separate list also
820 // ensure a client is either in m_clients or m_clientsAwaitingCallback.
821 HeapVector<Member<ResourceClient>> clientsToNotify;
822 copyToVector(m_clientsAwaitingCallback, clientsToNotify);
823
824 for (const auto& client : clientsToNotify) {
825 // Handle case (2) to skip removed clients.
826 if (!m_clientsAwaitingCallback.remove(client))
827 continue;
828 m_clients.add(client);
829
830 // When revalidation starts after waiting clients are scheduled and
831 // before they are added here. In such cases, we just add the clients
832 // to |m_clients| without didAddClient(), as in Resource::addClient().
833 if (!m_isRevalidating)
834 didAddClient(client);
835 }
836
837 // It is still possible for the above loop to finish a new client
838 // synchronously. If there's no client waiting we should deschedule.
839 bool scheduled = ResourceCallback::callbackHandler().isScheduled(this);
840 if (scheduled && m_clientsAwaitingCallback.isEmpty())
841 ResourceCallback::callbackHandler().cancel(this);
842
843 // Prevent the case when there are clients waiting but no callback scheduled.
844 DCHECK(m_clientsAwaitingCallback.isEmpty() || scheduled);
845 }
846
847 void Resource::prune() {
848 destroyDecodedDataIfPossible();
849 }
850
851 void Resource::onMemoryStateChange(MemoryState state) {
852 if (state != MemoryState::SUSPENDED)
853 return;
854 prune();
855 if (!m_cacheHandler)
856 return;
857 m_cacheHandler->clearCachedMetadata(CachedMetadataHandler::CacheLocally);
858 }
859
860 void Resource::onMemoryDump(WebMemoryDumpLevelOfDetail levelOfDetail,
861 WebProcessMemoryDump* memoryDump) const {
862 static const size_t kMaxURLReportLength = 128;
863 static const int kMaxResourceClientToShowInMemoryInfra = 10;
864
865 const String dumpName = getMemoryDumpName();
866 WebMemoryAllocatorDump* dump =
867 memoryDump->createMemoryAllocatorDump(dumpName);
868 dump->addScalar("encoded_size", "bytes", m_encodedSizeMemoryUsage);
869 if (hasClientsOrObservers())
870 dump->addScalar("live_size", "bytes", m_encodedSizeMemoryUsage);
871 else
872 dump->addScalar("dead_size", "bytes", m_encodedSizeMemoryUsage);
873
874 if (m_data)
875 m_data->onMemoryDump(dumpName, memoryDump);
876
877 if (levelOfDetail == WebMemoryDumpLevelOfDetail::Detailed) {
878 String urlToReport = url().getString();
879 if (urlToReport.length() > kMaxURLReportLength) {
880 urlToReport.truncate(kMaxURLReportLength);
881 urlToReport = urlToReport + "...";
882 }
883 dump->addString("url", "", urlToReport);
884
885 dump->addString("reason_not_deletable", "", reasonNotDeletable());
886
887 Vector<String> clientNames;
888 ResourceClientWalker<ResourceClient> walker(m_clients);
889 while (ResourceClient* client = walker.next())
890 clientNames.push_back(client->debugName());
891 ResourceClientWalker<ResourceClient> walker2(m_clientsAwaitingCallback);
892 while (ResourceClient* client = walker2.next())
893 clientNames.push_back("(awaiting) " + client->debugName());
894 ResourceClientWalker<ResourceClient> walker3(m_finishedClients);
895 while (ResourceClient* client = walker3.next())
896 clientNames.push_back("(finished) " + client->debugName());
897 std::sort(clientNames.begin(), clientNames.end(),
898 WTF::codePointCompareLessThan);
899
900 StringBuilder builder;
901 for (size_t i = 0;
902 i < clientNames.size() && i < kMaxResourceClientToShowInMemoryInfra;
903 ++i) {
904 if (i > 0)
905 builder.append(" / ");
906 builder.append(clientNames[i]);
907 }
908 if (clientNames.size() > kMaxResourceClientToShowInMemoryInfra) {
909 builder.append(" / and ");
910 builder.appendNumber(clientNames.size() -
911 kMaxResourceClientToShowInMemoryInfra);
912 builder.append(" more");
913 }
914 dump->addString("ResourceClient", "", builder.toString());
915 }
916
917 const String overheadName = dumpName + "/metadata";
918 WebMemoryAllocatorDump* overheadDump =
919 memoryDump->createMemoryAllocatorDump(overheadName);
920 overheadDump->addScalar("size", "bytes", overheadSize());
921 memoryDump->addSuballocation(
922 overheadDump->guid(), String(WTF::Partitions::kAllocatedObjectPoolName));
923 }
924
925 String Resource::getMemoryDumpName() const {
926 return String::format(
927 "web_cache/%s_resources/%ld",
928 resourceTypeToString(getType(), options().initiatorInfo.name),
929 m_identifier);
930 }
931
932 void Resource::setCachePolicyBypassingCache() {
933 m_resourceRequest.setCachePolicy(WebCachePolicy::BypassingCache);
934 }
935
936 void Resource::setPreviewsStateNoTransform() {
937 m_resourceRequest.setPreviewsState(WebURLRequest::PreviewsNoTransform);
938 }
939
940 void Resource::clearRangeRequestHeader() {
941 m_resourceRequest.clearHTTPHeaderField("range");
942 }
943
944 void Resource::revalidationSucceeded(
945 const ResourceResponse& validatingResponse) {
946 SECURITY_CHECK(m_redirectChain.isEmpty());
947 SECURITY_CHECK(equalIgnoringFragmentIdentifier(validatingResponse.url(),
948 response().url()));
949 m_response.setResourceLoadTiming(validatingResponse.resourceLoadTiming());
950
951 // RFC2616 10.3.5
952 // Update cached headers from the 304 response
953 const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields();
954 for (const auto& header : newHeaders) {
955 // Entity headers should not be sent by servers when generating a 304
956 // response; misconfigured servers send them anyway. We shouldn't allow such
957 // headers to update the original request. We'll base this on the list
958 // defined by RFC2616 7.1, with a few additions for extension headers we
959 // care about.
960 if (!shouldUpdateHeaderAfterRevalidation(header.key))
961 continue;
962 m_response.setHTTPHeaderField(header.key, header.value);
963 }
964
965 m_isRevalidating = false;
966 }
967
968 void Resource::revalidationFailed() {
969 SECURITY_CHECK(m_redirectChain.isEmpty());
970 clearData();
971 m_cacheHandler.clear();
972 destroyDecodedDataForFailedRevalidation();
973 m_isRevalidating = false;
974 }
975
976 bool Resource::canReuseRedirectChain() const {
977 for (auto& redirect : m_redirectChain) {
978 if (!canUseResponse(redirect.m_redirectResponse, m_responseTimestamp))
979 return false;
980 if (redirect.m_request.cacheControlContainsNoCache() ||
981 redirect.m_request.cacheControlContainsNoStore())
982 return false;
983 }
984 return true;
985 }
986
987 bool Resource::hasCacheControlNoStoreHeader() const {
988 return response().cacheControlContainsNoStore() ||
989 resourceRequest().cacheControlContainsNoStore();
990 }
991
992 bool Resource::hasVaryHeader() const {
993 return !response().httpHeaderField(HTTPNames::Vary).isNull();
994 }
995
996 bool Resource::mustRevalidateDueToCacheHeaders() const {
997 return !canUseResponse(response(), m_responseTimestamp) ||
998 resourceRequest().cacheControlContainsNoCache() ||
999 resourceRequest().cacheControlContainsNoStore();
1000 }
1001
1002 bool Resource::canUseCacheValidator() const {
1003 if (isLoading() || errorOccurred())
1004 return false;
1005
1006 if (hasCacheControlNoStoreHeader())
1007 return false;
1008
1009 // Do not revalidate Resource with redirects. https://crbug.com/613971
1010 if (!redirectChain().isEmpty())
1011 return false;
1012
1013 return response().hasCacheValidatorFields() ||
1014 resourceRequest().hasCacheValidatorFields();
1015 }
1016
1017 size_t Resource::calculateOverheadSize() const {
1018 static const int kAverageClientsHashMapSize = 384;
1019 return sizeof(Resource) + response().memoryUsage() +
1020 kAverageClientsHashMapSize +
1021 resourceRequest().url().getString().length() * 2;
1022 }
1023
1024 void Resource::didChangePriority(ResourceLoadPriority loadPriority,
1025 int intraPriorityValue) {
1026 m_resourceRequest.setPriority(loadPriority, intraPriorityValue);
1027 if (m_loader)
1028 m_loader->didChangePriority(loadPriority, intraPriorityValue);
1029 }
1030
1031 // TODO(toyoshim): Consider to generate automatically. https://crbug.com/675515.
1032 static const char* initiatorTypeNameToString(
1033 const AtomicString& initiatorTypeName) {
1034 if (initiatorTypeName == FetchInitiatorTypeNames::css)
1035 return "CSS resource";
1036 if (initiatorTypeName == FetchInitiatorTypeNames::document)
1037 return "Document";
1038 if (initiatorTypeName == FetchInitiatorTypeNames::icon)
1039 return "Icon";
1040 if (initiatorTypeName == FetchInitiatorTypeNames::internal)
1041 return "Internal resource";
1042 if (initiatorTypeName == FetchInitiatorTypeNames::link)
1043 return "Link element resource";
1044 if (initiatorTypeName == FetchInitiatorTypeNames::processinginstruction)
1045 return "Processing instruction";
1046 if (initiatorTypeName == FetchInitiatorTypeNames::texttrack)
1047 return "Text track";
1048 if (initiatorTypeName == FetchInitiatorTypeNames::xml)
1049 return "XML resource";
1050 if (initiatorTypeName == FetchInitiatorTypeNames::xmlhttprequest)
1051 return "XMLHttpRequest";
1052
1053 static_assert(
1054 FetchInitiatorTypeNames::FetchInitiatorTypeNamesCount == 12,
1055 "New FetchInitiatorTypeNames should be handled correctly here.");
1056
1057 return "Resource";
1058 }
1059
1060 const char* Resource::resourceTypeToString(
1061 Type type,
1062 const AtomicString& fetchInitiatorName) {
1063 switch (type) {
1064 case Resource::MainResource:
1065 return "Main resource";
1066 case Resource::Image:
1067 return "Image";
1068 case Resource::CSSStyleSheet:
1069 return "CSS stylesheet";
1070 case Resource::Script:
1071 return "Script";
1072 case Resource::Font:
1073 return "Font";
1074 case Resource::Raw:
1075 return initiatorTypeNameToString(fetchInitiatorName);
1076 case Resource::SVGDocument:
1077 return "SVG document";
1078 case Resource::XSLStyleSheet:
1079 return "XSL stylesheet";
1080 case Resource::LinkPrefetch:
1081 return "Link prefetch resource";
1082 case Resource::TextTrack:
1083 return "Text track";
1084 case Resource::ImportResource:
1085 return "Imported resource";
1086 case Resource::Media:
1087 return "Media";
1088 case Resource::Manifest:
1089 return "Manifest";
1090 case Resource::Mock:
1091 return "Mock";
1092 }
1093 NOTREACHED();
1094 return initiatorTypeNameToString(fetchInitiatorName);
1095 }
1096
1097 bool Resource::shouldBlockLoadEvent() const {
1098 return !m_linkPreload && isLoadEventBlockingResourceType();
1099 }
1100
1101 bool Resource::isLoadEventBlockingResourceType() const {
1102 switch (m_type) {
1103 case Resource::MainResource:
1104 case Resource::Image:
1105 case Resource::CSSStyleSheet:
1106 case Resource::Script:
1107 case Resource::Font:
1108 case Resource::SVGDocument:
1109 case Resource::XSLStyleSheet:
1110 case Resource::ImportResource:
1111 return true;
1112 case Resource::Raw:
1113 case Resource::LinkPrefetch:
1114 case Resource::TextTrack:
1115 case Resource::Media:
1116 case Resource::Manifest:
1117 case Resource::Mock:
1118 return false;
1119 }
1120 NOTREACHED();
1121 return false;
1122 }
1123
1124 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/fetch/Resource.h ('k') | third_party/WebKit/Source/core/fetch/ResourceClient.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698