OLD | NEW |
| (Empty) |
1 /* | |
2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) | |
3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) | |
4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) | |
5 Copyright (C) 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 | |
OLD | NEW |