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 rights reserved.
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25 #include "core/fetch/Resource.h"
26
27 #include "core/FetchInitiatorTypeNames.h"
28 #include "core/fetch/CachedMetadata.h"
29 #include "core/fetch/CrossOriginAccessControl.h"
30 #include "core/fetch/MemoryCache.h"
31 #include "core/fetch/ResourceClient.h"
32 #include "core/fetch/ResourceClientWalker.h"
33 #include "core/fetch/ResourceFetcher.h"
34 #include "core/fetch/ResourceLoader.h"
35 #include "core/fetch/ResourcePtr.h"
36 #include "core/inspector/InspectorInstrumentation.h"
37 #include "platform/Logging.h"
38 #include "platform/SharedBuffer.h"
39 #include "platform/TraceEvent.h"
40 #include "platform/weborigin/KURL.h"
41 #include "public/platform/Platform.h"
42 #include "wtf/CurrentTime.h"
43 #include "wtf/MathExtras.h"
44 #include "wtf/RefCountedLeakCounter.h"
45 #include "wtf/StdLibExtras.h"
46 #include "wtf/Vector.h"
47 #include "wtf/text/CString.h"
48
49 using namespace WTF;
50
51 namespace blink {
52
53 // These response headers are not copied from a revalidated response to the
54 // cached response headers. For compatibility, this list is based on Chromium's
55 // net/http/http_response_headers.cc.
56 const char* const headersToIgnoreAfterRevalidation[] = {
57 "allow",
58 "connection",
59 "etag",
60 "expires",
61 "keep-alive",
62 "last-modified"
63 "proxy-authenticate",
64 "proxy-connection",
65 "trailer",
66 "transfer-encoding",
67 "upgrade",
68 "www-authenticate",
69 "x-frame-options",
70 "x-xss-protection",
71 };
72
73 // Some header prefixes mean "Don't copy this header from a 304 response.".
74 // Rather than listing all the relevant headers, we can consolidate them into
75 // this list, also grabbed from Chromium's net/http/http_response_headers.cc.
76 const char* const headerPrefixesToIgnoreAfterRevalidation[] = {
77 "content-",
78 "x-content-",
79 "x-webkit-"
80 };
81
shouldUpdateHeaderAfterRevalidation(const AtomicString & header)82 static inline bool shouldUpdateHeaderAfterRevalidation(const AtomicString& header)
83 {
84 for (size_t i = 0; i < WTF_ARRAY_LENGTH(headersToIgnoreAfterRevalidation); i++) {
85 if (equalIgnoringCase(header, headersToIgnoreAfterRevalidation[i]))
86 return false;
87 }
88 for (size_t i = 0; i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) {
89 if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i], false))
90 return false;
91 }
92 return true;
93 }
94
95 DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("Resource"));
96 unsigned Resource::s_instanceCount = 0;
97
Resource(const ResourceRequest & request,Type type)98 Resource::Resource(const ResourceRequest& request, Type type)
99 : m_resourceRequest(request)
100 , m_responseTimestamp(currentTime())
101 , m_cancelTimer(this, &Resource::cancelTimerFired)
102 , m_loadFinishTime(0)
103 , m_identifier(0)
104 , m_encodedSize(0)
105 , m_decodedSize(0)
106 , m_handleCount(0)
107 , m_preloadCount(0)
108 , m_protectorCount(0)
109 , m_preloadResult(PreloadNotReferenced)
110 , m_requestedFromNetworkingLayer(false)
111 , m_loading(false)
112 , m_switchingClientsToRevalidatedResource(false)
113 , m_type(type)
114 , m_status(Pending)
115 , m_wasPurged(false)
116 , m_needsSynchronousCacheHit(false)
117 #ifdef ENABLE_RESOURCE_IS_DELETED_CHECK
118 , m_deleted(false)
119 #endif
120 , m_resourceToRevalidate(nullptr)
121 , m_proxyResource(nullptr)
122 {
123 ASSERT(m_type == unsigned(type)); // m_type is a bitfield, so this tests careless updates of the enum.
124 ++s_instanceCount;
125 #ifndef NDEBUG
126 cachedResourceLeakCounter.increment();
127 #endif
128 memoryCache()->registerLiveResource(*this);
129
130 if (!m_resourceRequest.url().hasFragmentIdentifier())
131 return;
132 KURL urlForCache = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url());
133 if (urlForCache.hasFragmentIdentifier())
134 return;
135 m_fragmentIdentifierForRequest = m_resourceRequest.url().fragmentIdentifier();
136 m_resourceRequest.setURL(urlForCache);
137 }
138
~Resource()139 Resource::~Resource()
140 {
141 ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this.
142 ASSERT(canDelete());
143 RELEASE_ASSERT(!memoryCache()->contains(this));
144 RELEASE_ASSERT(!ResourceCallback::callbackHandler()->isScheduled(this));
145 ASSERT(url().isNull() || memoryCache()->resourceForURL(KURL(ParsedURLString, url())) != this);
146 assertAlive();
147
148 #ifdef ENABLE_RESOURCE_IS_DELETED_CHECK
149 m_deleted = true;
150 #endif
151 #ifndef NDEBUG
152 cachedResourceLeakCounter.decrement();
153 #endif
154 --s_instanceCount;
155 }
156
dispose()157 void Resource::dispose()
158 {
159 }
160
trace(Visitor * visitor)161 void Resource::trace(Visitor* visitor)
162 {
163 visitor->trace(m_loader);
164 visitor->trace(m_resourceToRevalidate);
165 visitor->trace(m_proxyResource);
166 }
167
failBeforeStarting()168 void Resource::failBeforeStarting()
169 {
170 WTF_LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data());
171 error(Resource::LoadError);
172 }
173
load(ResourceFetcher * fetcher,const ResourceLoaderOptions & options)174 void Resource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options)
175 {
176 if (!fetcher->frame()) {
177 failBeforeStarting();
178 return;
179 }
180
181 m_options = options;
182 m_loading = true;
183
184 if (!accept().isEmpty())
185 m_resourceRequest.setHTTPAccept(accept());
186
187 // FIXME: It's unfortunate that the cache layer and below get to know anything about fragment identifiers.
188 // We should look into removing the expectation of that knowledge from the platform network stacks.
189 ResourceRequest request(m_resourceRequest);
190 if (!m_fragmentIdentifierForRequest.isNull()) {
191 KURL url = request.url();
192 url.setFragmentIdentifier(m_fragmentIdentifierForRequest);
193 request.setURL(url);
194 m_fragmentIdentifierForRequest = String();
195 }
196 m_status = Pending;
197 if (m_loader) {
198 RELEASE_ASSERT(m_options.synchronousPolicy == RequestSynchronously);
199 m_loader->changeToSynchronous();
200 return;
201 }
202 m_loader = ResourceLoader::create(fetcher, this, request, options);
203 m_loader->start();
204 }
205
checkNotify()206 void Resource::checkNotify()
207 {
208 if (isLoading())
209 return;
210
211 ResourceClientWalker<ResourceClient> w(m_clients);
212 while (ResourceClient* c = w.next())
213 c->notifyFinished(this);
214 }
215
appendData(const char * data,int length)216 void Resource::appendData(const char* data, int length)
217 {
218 TRACE_EVENT0("blink", "Resource::appendData");
219 ASSERT(!m_resourceToRevalidate);
220 ASSERT(!errorOccurred());
221 if (m_options.dataBufferingPolicy == DoNotBufferData)
222 return;
223 if (m_data)
224 m_data->append(data, length);
225 else
226 m_data = SharedBuffer::createPurgeable(data, length);
227 setEncodedSize(m_data->size());
228 }
229
setResourceBuffer(PassRefPtr<SharedBuffer> resourceBuffer)230 void Resource::setResourceBuffer(PassRefPtr<SharedBuffer> resourceBuffer)
231 {
232 ASSERT(!m_resourceToRevalidate);
233 ASSERT(!errorOccurred());
234 ASSERT(m_options.dataBufferingPolicy == BufferData);
235 m_data = resourceBuffer;
236 setEncodedSize(m_data->size());
237 }
238
setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy)239 void Resource::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy)
240 {
241 m_options.dataBufferingPolicy = dataBufferingPolicy;
242 m_data.clear();
243 setEncodedSize(0);
244 }
245
error(Resource::Status status)246 void Resource::error(Resource::Status status)
247 {
248 if (m_resourceToRevalidate)
249 revalidationFailed();
250
251 if (!m_error.isNull() && (m_error.isCancellation() || !isPreloaded()))
252 memoryCache()->remove(this);
253
254 setStatus(status);
255 ASSERT(errorOccurred());
256 m_data.clear();
257
258 setLoading(false);
259 checkNotify();
260 }
261
finishOnePart()262 void Resource::finishOnePart()
263 {
264 setLoading(false);
265 checkNotify();
266 }
267
finish(double finishTime)268 void Resource::finish(double finishTime)
269 {
270 ASSERT(!m_resourceToRevalidate);
271 ASSERT(!errorOccurred());
272 m_loadFinishTime = finishTime;
273 finishOnePart();
274 if (!errorOccurred())
275 m_status = Cached;
276 }
277
passesAccessControlCheck(SecurityOrigin * securityOrigin)278 bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin)
279 {
280 String ignoredErrorDescription;
281 return passesAccessControlCheck(securityOrigin, ignoredErrorDescription);
282 }
283
passesAccessControlCheck(SecurityOrigin * securityOrigin,String & errorDescription)284 bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin, String& errorDescription)
285 {
286 return blink::passesAccessControlCheck(m_response, resourceRequest().allowStoredCredentials() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription);
287 }
288
currentAge(const ResourceResponse & response,double responseTimestamp)289 static double currentAge(const ResourceResponse& response, double responseTimestamp)
290 {
291 // RFC2616 13.2.3
292 // No compensation for latency as that is not terribly important in practice
293 double dateValue = response.date();
294 double apparentAge = std::isfinite(dateValue) ? std::max(0., responseTimestamp - dateValue) : 0;
295 double ageValue = response.age();
296 double correctedReceivedAge = std::isfinite(ageValue) ? std::max(apparentAge, ageValue) : apparentAge;
297 double residentTime = currentTime() - responseTimestamp;
298 return correctedReceivedAge + residentTime;
299 }
300
freshnessLifetime(ResourceResponse & response,double responseTimestamp)301 static double freshnessLifetime(ResourceResponse& response, double responseTimestamp)
302 {
303 #if !OS(ANDROID)
304 // On desktop, local files should be reloaded in case they change.
305 if (response.url().isLocalFile())
306 return 0;
307 #endif
308
309 // Cache other non-http / non-filesystem resources liberally.
310 if (!response.url().protocolIsInHTTPFamily()
311 && !response.url().protocolIs("filesystem"))
312 return std::numeric_limits<double>::max();
313
314 // RFC2616 13.2.4
315 double maxAgeValue = response.cacheControlMaxAge();
316 if (std::isfinite(maxAgeValue))
317 return maxAgeValue;
318 double expiresValue = response.expires();
319 double dateValue = response.date();
320 double creationTime = std::isfinite(dateValue) ? dateValue : responseTimestamp;
321 if (std::isfinite(expiresValue))
322 return expiresValue - creationTime;
323 double lastModifiedValue = response.lastModified();
324 if (std::isfinite(lastModifiedValue))
325 return (creationTime - lastModifiedValue) * 0.1;
326 // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0.
327 return 0;
328 }
329
canUseResponse(ResourceResponse & response,double responseTimestamp)330 static bool canUseResponse(ResourceResponse& response, double responseTimestamp)
331 {
332 if (response.isNull())
333 return false;
334
335 // FIXME: Why isn't must-revalidate considered a reason we can't use the response?
336 if (response.cacheControlContainsNoCache() || response.cacheControlContainsNoStore())
337 return false;
338
339 if (response.httpStatusCode() == 303) {
340 // Must not be cached.
341 return false;
342 }
343
344 if (response.httpStatusCode() == 302 || response.httpStatusCode() == 307) {
345 // Default to not cacheable unless explicitly allowed.
346 bool hasMaxAge = std::isfinite(response.cacheControlMaxAge());
347 bool hasExpires = std::isfinite(response.expires());
348 // TODO: consider catching Cache-Control "private" and "public" here.
349 if (!hasMaxAge && !hasExpires)
350 return false;
351 }
352
353 return currentAge(response, responseTimestamp) <= freshnessLifetime(response, responseTimestamp);
354 }
355
lastResourceRequest() const356 const ResourceRequest& Resource::lastResourceRequest() const
357 {
358 if (!m_redirectChain.size())
359 return m_resourceRequest;
360 return m_redirectChain.last().m_request;
361 }
362
willSendRequest(ResourceRequest & request,const ResourceResponse & response)363 void Resource::willSendRequest(ResourceRequest& request, const ResourceResponse& response)
364 {
365 m_redirectChain.append(RedirectPair(request, response));
366 m_requestedFromNetworkingLayer = true;
367 }
368
unlock()369 bool Resource::unlock()
370 {
371 if (!m_data)
372 return false;
373
374 if (!m_data->isLocked())
375 return true;
376
377 if (!memoryCache()->contains(this) || hasClients() || m_handleCount > 1 || m_proxyResource || m_resourceToRevalidate || !m_loadFinishTime || !isSafeToUnlock())
378 return false;
379
380 m_data->unlock();
381 return true;
382 }
383
hasRightHandleCountApartFromCache(unsigned targetCount) const384 bool Resource::hasRightHandleCountApartFromCache(unsigned targetCount) const
385 {
386 return m_handleCount == targetCount + (memoryCache()->contains(this) ? 1 : 0);
387 }
388
responseReceived(const ResourceResponse & response)389 void Resource::responseReceived(const ResourceResponse& response)
390 {
391 setResponse(response);
392 m_responseTimestamp = currentTime();
393 String encoding = response.textEncodingName();
394 if (!encoding.isNull())
395 setEncoding(encoding);
396
397 if (!m_resourceToRevalidate)
398 return;
399 if (response.httpStatusCode() == 304)
400 revalidationSucceeded(response);
401 else
402 revalidationFailed();
403 }
404
setSerializedCachedMetadata(const char * data,size_t size)405 void Resource::setSerializedCachedMetadata(const char* data, size_t size)
406 {
407 // We only expect to receive cached metadata from the platform once.
408 // If this triggers, it indicates an efficiency problem which is most
409 // likely unexpected in code designed to improve performance.
410 ASSERT(!m_cachedMetadata);
411 ASSERT(!m_resourceToRevalidate);
412
413 m_cachedMetadata = CachedMetadata::deserialize(data, size);
414 }
415
setCachedMetadata(unsigned dataTypeID,const char * data,size_t size,MetadataCacheType cacheType)416 void Resource::setCachedMetadata(unsigned dataTypeID, const char* data, size_t size, MetadataCacheType cacheType)
417 {
418 // Currently, only one type of cached metadata per resource is supported.
419 // If the need arises for multiple types of metadata per resource this could
420 // be enhanced to store types of metadata in a map.
421 ASSERT(!m_cachedMetadata);
422
423 m_cachedMetadata = CachedMetadata::create(dataTypeID, data, size);
424
425 if (cacheType == SendToPlatform) {
426 const Vector<char>& serializedData = m_cachedMetadata->serialize();
427 blink::Platform::current()->cacheMetadata(m_response.url(), m_response.responseTime(), serializedData.data(), serializedData.size());
428 }
429 }
430
clearCachedMetadata()431 void Resource::clearCachedMetadata()
432 {
433 m_cachedMetadata.clear();
434 }
435
canDelete() const436 bool Resource::canDelete() const
437 {
438 return !hasClients() && !m_loader && !m_preloadCount && hasRightHandleCountApartFromCache(0)
439 && !m_protectorCount && !m_resourceToRevalidate && !m_proxyResource;
440 }
441
hasOneHandle() const442 bool Resource::hasOneHandle() const
443 {
444 return hasRightHandleCountApartFromCache(1);
445 }
446
cachedMetadata(unsigned dataTypeID) const447 CachedMetadata* Resource::cachedMetadata(unsigned dataTypeID) const
448 {
449 if (!m_cachedMetadata || m_cachedMetadata->dataTypeID() != dataTypeID)
450 return 0;
451 return m_cachedMetadata.get();
452 }
453
clearLoader()454 void Resource::clearLoader()
455 {
456 m_loader = nullptr;
457 }
458
addClient(ResourceClient * client)459 void Resource::addClient(ResourceClient* client)
460 {
461 if (addClientToSet(client))
462 didAddClient(client);
463 }
464
didAddClient(ResourceClient * c)465 void Resource::didAddClient(ResourceClient* c)
466 {
467 if (!isLoading() && !stillNeedsLoad())
468 c->notifyFinished(this);
469 }
470
shouldSendCachedDataSynchronouslyForType(Resource::Type type)471 static bool shouldSendCachedDataSynchronouslyForType(Resource::Type type)
472 {
473 // Some resources types default to return data synchronously.
474 // For most of these, it's because there are layout tests that
475 // expect data to return synchronously in case of cache hit. In
476 // the case of fonts, there was a performance regression.
477 // FIXME: Get to the point where we don't need to special-case sync/async
478 // behavior for different resource types.
479 if (type == Resource::Image)
480 return true;
481 if (type == Resource::CSSStyleSheet)
482 return true;
483 if (type == Resource::Script)
484 return true;
485 if (type == Resource::Font)
486 return true;
487 return false;
488 }
489
addClientToSet(ResourceClient * client)490 bool Resource::addClientToSet(ResourceClient* client)
491 {
492 ASSERT(!isPurgeable());
493
494 if (m_preloadResult == PreloadNotReferenced) {
495 if (isLoaded())
496 m_preloadResult = PreloadReferencedWhileComplete;
497 else if (m_requestedFromNetworkingLayer)
498 m_preloadResult = PreloadReferencedWhileLoading;
499 else
500 m_preloadResult = PreloadReferenced;
501 }
502 if (!hasClients())
503 memoryCache()->makeLive(this);
504
505 // If we have existing data to send to the new client and the resource type supprts it, send it asynchronously.
506 if (!m_response.isNull() && !m_proxyResource && !shouldSendCachedDataSynchronouslyForType(type()) && !m_needsSynchronousCacheHit) {
507 m_clientsAwaitingCallback.add(client);
508 ResourceCallback::callbackHandler()->schedule(this);
509 return false;
510 }
511
512 m_clients.add(client);
513 return true;
514 }
515
removeClient(ResourceClient * client)516 void Resource::removeClient(ResourceClient* client)
517 {
518 if (m_clientsAwaitingCallback.contains(client)) {
519 ASSERT(!m_clients.contains(client));
520 m_clientsAwaitingCallback.remove(client);
521 } else {
522 ASSERT(m_clients.contains(client));
523 m_clients.remove(client);
524 didRemoveClient(client);
525 }
526
527 if (m_clientsAwaitingCallback.isEmpty())
528 ResourceCallback::callbackHandler()->cancel(this);
529
530 bool deleted = deleteIfPossible();
531 if (!deleted && !hasClients()) {
532 memoryCache()->makeDead(this);
533 if (!m_switchingClientsToRevalidatedResource)
534 allClientsRemoved();
535
536 // RFC2616 14.9.2:
537 // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
538 // "... History buffers MAY store such responses as part of their normal operation."
539 // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
540 if (hasCacheControlNoStoreHeader() && url().protocolIs("https")) {
541 memoryCache()->remove(this);
542 memoryCache()->prune();
543 } else {
544 memoryCache()->prune(this);
545 }
546 }
547 // This object may be dead here.
548 }
549
allClientsRemoved()550 void Resource::allClientsRemoved()
551 {
552 if (!m_loader)
553 return;
554 if (m_type == MainResource || m_type == Raw)
555 cancelTimerFired(&m_cancelTimer);
556 else if (!m_cancelTimer.isActive())
557 m_cancelTimer.startOneShot(0, FROM_HERE);
558
559 unlock();
560 }
561
cancelTimerFired(Timer<Resource> * timer)562 void Resource::cancelTimerFired(Timer<Resource>* timer)
563 {
564 ASSERT_UNUSED(timer, timer == &m_cancelTimer);
565 if (hasClients() || !m_loader)
566 return;
567 ResourcePtr<Resource> protect(this);
568 m_loader->cancelIfNotFinishing();
569 if (m_status != Cached)
570 memoryCache()->remove(this);
571 }
572
deleteIfPossible()573 bool Resource::deleteIfPossible()
574 {
575 if (canDelete() && !memoryCache()->contains(this)) {
576 InspectorInstrumentation::willDestroyResource(this);
577 dispose();
578 memoryCache()->unregisterLiveResource(*this);
579 #if !ENABLE(OILPAN)
580 delete this;
581 #endif
582 return true;
583 }
584 return false;
585 }
586
setDecodedSize(size_t decodedSize)587 void Resource::setDecodedSize(size_t decodedSize)
588 {
589 if (decodedSize == m_decodedSize)
590 return;
591 size_t oldSize = size();
592 m_decodedSize = decodedSize;
593 memoryCache()->update(this, oldSize, size());
594 memoryCache()->updateDecodedResource(this, UpdateForPropertyChange);
595 }
596
setEncodedSize(size_t encodedSize)597 void Resource::setEncodedSize(size_t encodedSize)
598 {
599 if (encodedSize == m_encodedSize)
600 return;
601 size_t oldSize = size();
602 m_encodedSize = encodedSize;
603 memoryCache()->update(this, oldSize, size());
604 }
605
didAccessDecodedData()606 void Resource::didAccessDecodedData()
607 {
608 memoryCache()->updateDecodedResource(this, UpdateForAccess);
609 memoryCache()->prune();
610 }
611
finishPendingClients()612 void Resource::finishPendingClients()
613 {
614 // We're going to notify clients one by one. It is simple if the client does nothing.
615 // However there are a couple other things that can happen.
616 //
617 // 1. Clients can be added during the loop. Make sure they are not processed.
618 // 2. Clients can be removed during the loop. Make sure they are always available to be
619 // removed. Also don't call removed clients or add them back.
620
621 // Handle case (1) by saving a list of clients to notify. A separate list also ensure
622 // a client is either in m_clients or m_clientsAwaitingCallback.
623 Vector<ResourceClient*> clientsToNotify;
624 copyToVector(m_clientsAwaitingCallback, clientsToNotify);
625
626 for (size_t i = 0; i < clientsToNotify.size(); ++i) {
627 ResourceClient* client = clientsToNotify[i];
628
629 // Handle case (2) to skip removed clients.
630 if (!m_clientsAwaitingCallback.remove(client))
631 continue;
632 m_clients.add(client);
633 didAddClient(client);
634 }
635
636 // It is still possible for the above loop to finish a new client synchronously.
637 // If there's no client waiting we should deschedule.
638 bool scheduled = ResourceCallback::callbackHandler()->isScheduled(this);
639 if (scheduled && m_clientsAwaitingCallback.isEmpty())
640 ResourceCallback::callbackHandler()->cancel(this);
641
642 // Prevent the case when there are clients waiting but no callback scheduled.
643 ASSERT(m_clientsAwaitingCallback.isEmpty() || scheduled);
644 }
645
prune()646 void Resource::prune()
647 {
648 destroyDecodedDataIfPossible();
649 unlock();
650 }
651
setResourceToRevalidate(Resource * resource)652 void Resource::setResourceToRevalidate(Resource* resource)
653 {
654 ASSERT(resource);
655 ASSERT(!m_resourceToRevalidate);
656 ASSERT(resource != this);
657 ASSERT(m_handlesToRevalidate.isEmpty());
658 ASSERT(resource->type() == type());
659
660 WTF_LOG(ResourceLoading, "Resource %p setResourceToRevalidate %p", this, resource);
661
662 // The following assert should be investigated whenever it occurs. Although it should never fire, it currently does in rare circumstances.
663 // https://bugs.webkit.org/show_bug.cgi?id=28604.
664 // So the code needs to be robust to this assert failing thus the "if (m_resourceToRevalidate->m_proxyResource == this)" in Resource::clearResourceToRevalidate.
665 ASSERT(!resource->m_proxyResource);
666
667 resource->m_proxyResource = this;
668 m_resourceToRevalidate = resource;
669 }
670
clearResourceToRevalidate()671 void Resource::clearResourceToRevalidate()
672 {
673 ASSERT(m_resourceToRevalidate);
674 if (m_switchingClientsToRevalidatedResource)
675 return;
676
677 // A resource may start revalidation before this method has been called, so check that this resource is still the proxy resource before clearing it out.
678 if (m_resourceToRevalidate->m_proxyResource == this) {
679 m_resourceToRevalidate->m_proxyResource = nullptr;
680 m_resourceToRevalidate->deleteIfPossible();
681 }
682 m_handlesToRevalidate.clear();
683 m_resourceToRevalidate = nullptr;
684 deleteIfPossible();
685 }
686
switchClientsToRevalidatedResource()687 void Resource::switchClientsToRevalidatedResource()
688 {
689 ASSERT(m_resourceToRevalidate);
690 ASSERT(memoryCache()->contains(m_resourceToRevalidate));
691 ASSERT(!memoryCache()->contains(this));
692
693 WTF_LOG(ResourceLoading, "Resource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate.get());
694
695 m_resourceToRevalidate->m_identifier = m_identifier;
696
697 m_switchingClientsToRevalidatedResource = true;
698 HashSet<ResourcePtrBase*>::iterator end = m_handlesToRevalidate.end();
699 for (HashSet<ResourcePtrBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) {
700 ResourcePtrBase* handle = *it;
701 handle->m_resource = m_resourceToRevalidate;
702 m_resourceToRevalidate->registerHandle(handle);
703 --m_handleCount;
704 }
705 ASSERT(!m_handleCount);
706 m_handlesToRevalidate.clear();
707
708 Vector<ResourceClient*> clientsToMove;
709 HashCountedSet<ResourceClient*>::iterator end2 = m_clients.end();
710 for (HashCountedSet<ResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) {
711 ResourceClient* client = it->key;
712 unsigned count = it->value;
713 while (count) {
714 clientsToMove.append(client);
715 --count;
716 }
717 }
718
719 unsigned moveCount = clientsToMove.size();
720 for (unsigned n = 0; n < moveCount; ++n)
721 removeClient(clientsToMove[n]);
722 ASSERT(m_clients.isEmpty());
723
724 for (unsigned n = 0; n < moveCount; ++n)
725 m_resourceToRevalidate->addClientToSet(clientsToMove[n]);
726 for (unsigned n = 0; n < moveCount; ++n) {
727 // Calling didAddClient may do anything, including trying to cancel revalidation.
728 // Assert that it didn't succeed.
729 ASSERT(m_resourceToRevalidate);
730 // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore.
731 if (m_resourceToRevalidate->m_clients.contains(clientsToMove[n]))
732 m_resourceToRevalidate->didAddClient(clientsToMove[n]);
733 }
734 m_switchingClientsToRevalidatedResource = false;
735 }
736
updateResponseAfterRevalidation(const ResourceResponse & validatingResponse)737 void Resource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse)
738 {
739 m_responseTimestamp = currentTime();
740
741 // RFC2616 10.3.5
742 // Update cached headers from the 304 response
743 const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields();
744 HTTPHeaderMap::const_iterator end = newHeaders.end();
745 for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) {
746 // Entity headers should not be sent by servers when generating a 304
747 // response; misconfigured servers send them anyway. We shouldn't allow
748 // such headers to update the original request. We'll base this on the
749 // list defined by RFC2616 7.1, with a few additions for extension headers
750 // we care about.
751 if (!shouldUpdateHeaderAfterRevalidation(it->key))
752 continue;
753 m_response.setHTTPHeaderField(it->key, it->value);
754 }
755 }
756
revalidationSucceeded(const ResourceResponse & response)757 void Resource::revalidationSucceeded(const ResourceResponse& response)
758 {
759 ASSERT(m_resourceToRevalidate);
760 ASSERT(!memoryCache()->contains(m_resourceToRevalidate));
761 ASSERT(m_resourceToRevalidate->isLoaded());
762
763 // Calling evict() can potentially delete revalidatingResource, which we use
764 // below. This mustn't be the case since revalidation means it is loaded
765 // and so canDelete() is false.
766 ASSERT(!canDelete());
767
768 m_resourceToRevalidate->updateResponseAfterRevalidation(response);
769 memoryCache()->replace(m_resourceToRevalidate, this);
770
771 switchClientsToRevalidatedResource();
772 assertAlive();
773 // clearResourceToRevalidate deletes this.
774 clearResourceToRevalidate();
775 }
776
revalidationFailed()777 void Resource::revalidationFailed()
778 {
779 ASSERT(WTF::isMainThread());
780 WTF_LOG(ResourceLoading, "Revalidation failed for %p", this);
781 ASSERT(resourceToRevalidate());
782 clearResourceToRevalidate();
783 }
784
registerHandle(ResourcePtrBase * h)785 void Resource::registerHandle(ResourcePtrBase* h)
786 {
787 assertAlive();
788 ++m_handleCount;
789 if (m_resourceToRevalidate)
790 m_handlesToRevalidate.add(h);
791 }
792
unregisterHandle(ResourcePtrBase * h)793 void Resource::unregisterHandle(ResourcePtrBase* h)
794 {
795 assertAlive();
796 ASSERT(m_handleCount > 0);
797 --m_handleCount;
798
799 if (m_resourceToRevalidate)
800 m_handlesToRevalidate.remove(h);
801
802 if (!m_handleCount) {
803 if (deleteIfPossible())
804 return;
805 unlock();
806 } else if (m_handleCount == 1 && memoryCache()->contains(this)) {
807 unlock();
808 if (!hasClients())
809 memoryCache()->prune(this);
810 }
811 }
812
canReuseRedirectChain()813 bool Resource::canReuseRedirectChain()
814 {
815 for (size_t i = 0; i < m_redirectChain.size(); ++i) {
816 if (!canUseResponse(m_redirectChain[i].m_redirectResponse, m_responseTimestamp))
817 return false;
818 if (m_redirectChain[i].m_request.cacheControlContainsNoCache() || m_redirectChain[i].m_request.cacheControlContainsNoStore())
819 return false;
820 }
821 return true;
822 }
823
hasCacheControlNoStoreHeader()824 bool Resource::hasCacheControlNoStoreHeader()
825 {
826 return m_response.cacheControlContainsNoStore() || m_resourceRequest.cacheControlContainsNoStore();
827 }
828
mustRevalidateDueToCacheHeaders()829 bool Resource::mustRevalidateDueToCacheHeaders()
830 {
831 return !canUseResponse(m_response, m_responseTimestamp) || m_resourceRequest.cacheControlContainsNoCache() || m_resourceRequest.cacheControlContainsNoStore();
832 }
833
canUseCacheValidator()834 bool Resource::canUseCacheValidator()
835 {
836 if (m_loading || errorOccurred())
837 return false;
838
839 if (hasCacheControlNoStoreHeader())
840 return false;
841 return m_response.hasCacheValidatorFields() || m_resourceRequest.hasCacheValidatorFields();
842 }
843
isPurgeable() const844 bool Resource::isPurgeable() const
845 {
846 return m_data && !m_data->isLocked();
847 }
848
wasPurged() const849 bool Resource::wasPurged() const
850 {
851 return m_wasPurged;
852 }
853
lock()854 bool Resource::lock()
855 {
856 if (!m_data)
857 return true;
858 if (m_data->isLocked())
859 return true;
860
861 ASSERT(!hasClients());
862
863 if (!m_data->lock()) {
864 m_wasPurged = true;
865 return false;
866 }
867 return true;
868 }
869
overheadSize() const870 size_t Resource::overheadSize() const
871 {
872 static const int kAverageClientsHashMapSize = 384;
873 return sizeof(Resource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2;
874 }
875
didChangePriority(ResourceLoadPriority loadPriority,int intraPriorityValue)876 void Resource::didChangePriority(ResourceLoadPriority loadPriority, int intraPriorityValue)
877 {
878 if (m_loader)
879 m_loader->didChangePriority(loadPriority, intraPriorityValue);
880 }
881
callbackHandler()882 Resource::ResourceCallback* Resource::ResourceCallback::callbackHandler()
883 {
884 DEFINE_STATIC_LOCAL(ResourceCallback, callbackHandler, ());
885 return &callbackHandler;
886 }
887
ResourceCallback()888 Resource::ResourceCallback::ResourceCallback()
889 : m_callbackTimer(this, &ResourceCallback::timerFired)
890 {
891 }
892
schedule(Resource * resource)893 void Resource::ResourceCallback::schedule(Resource* resource)
894 {
895 if (!m_callbackTimer.isActive())
896 m_callbackTimer.startOneShot(0, FROM_HERE);
897 resource->assertAlive();
898 m_resourcesWithPendingClients.add(resource);
899 }
900
cancel(Resource * resource)901 void Resource::ResourceCallback::cancel(Resource* resource)
902 {
903 resource->assertAlive();
904 m_resourcesWithPendingClients.remove(resource);
905 if (m_callbackTimer.isActive() && m_resourcesWithPendingClients.isEmpty())
906 m_callbackTimer.stop();
907 }
908
isScheduled(Resource * resource) const909 bool Resource::ResourceCallback::isScheduled(Resource* resource) const
910 {
911 return m_resourcesWithPendingClients.contains(resource);
912 }
913
timerFired(Timer<ResourceCallback> *)914 void Resource::ResourceCallback::timerFired(Timer<ResourceCallback>*)
915 {
916 HashSet<Resource*>::iterator end = m_resourcesWithPendingClients.end();
917 Vector<ResourcePtr<Resource> > resources;
918 for (HashSet<Resource*>::iterator it = m_resourcesWithPendingClients.begin(); it != end; ++it)
919 resources.append(*it);
920 m_resourcesWithPendingClients.clear();
921
922 for (size_t i = 0; i < resources.size(); i++) {
923 resources[i]->assertAlive();
924 resources[i]->finishPendingClients();
925 resources[i]->assertAlive();
926 }
927
928 for (size_t i = 0; i < resources.size(); i++)
929 resources[i]->assertAlive();
930 }
931
initatorTypeNameToString(const AtomicString & initiatorTypeName)932 static const char* initatorTypeNameToString(const AtomicString& initiatorTypeName)
933 {
934 if (initiatorTypeName == FetchInitiatorTypeNames::css)
935 return "CSS resource";
936 if (initiatorTypeName == FetchInitiatorTypeNames::document)
937 return "Document";
938 if (initiatorTypeName == FetchInitiatorTypeNames::icon)
939 return "Icon";
940 if (initiatorTypeName == FetchInitiatorTypeNames::internal)
941 return "Internal resource";
942 if (initiatorTypeName == FetchInitiatorTypeNames::link)
943 return "Link element resource";
944 if (initiatorTypeName == FetchInitiatorTypeNames::processinginstruction)
945 return "Processing instruction";
946 if (initiatorTypeName == FetchInitiatorTypeNames::texttrack)
947 return "Text track";
948 if (initiatorTypeName == FetchInitiatorTypeNames::xml)
949 return "XML resource";
950 if (initiatorTypeName == FetchInitiatorTypeNames::xmlhttprequest)
951 return "XMLHttpRequest";
952
953 return "Resource";
954 }
955
resourceTypeToString(Type type,const FetchInitiatorInfo & initiatorInfo)956 const char* Resource::resourceTypeToString(Type type, const FetchInitiatorInfo& initiatorInfo)
957 {
958 switch (type) {
959 case Resource::MainResource:
960 return "Main resource";
961 case Resource::Image:
962 return "Image";
963 case Resource::CSSStyleSheet:
964 return "CSS stylesheet";
965 case Resource::Script:
966 return "Script";
967 case Resource::Font:
968 return "Font";
969 case Resource::Raw:
970 return initatorTypeNameToString(initiatorInfo.name);
971 case Resource::SVGDocument:
972 return "SVG document";
973 case Resource::XSLStyleSheet:
974 return "XSL stylesheet";
975 case Resource::LinkPrefetch:
976 return "Link prefetch resource";
977 case Resource::LinkSubresource:
978 return "Link subresource";
979 case Resource::TextTrack:
980 return "Text track";
981 case Resource::ImportResource:
982 return "Imported resource";
983 case Resource::Media:
984 return "Media";
985 }
986 ASSERT_NOT_REACHED();
987 return initatorTypeNameToString(initiatorInfo.name);
988 }
989
990 #if !LOG_DISABLED
ResourceTypeName(Resource::Type type)991 const char* ResourceTypeName(Resource::Type type)
992 {
993 switch (type) {
994 case Resource::MainResource:
995 return "MainResource";
996 case Resource::Image:
997 return "Image";
998 case Resource::CSSStyleSheet:
999 return "CSSStyleSheet";
1000 case Resource::Script:
1001 return "Script";
1002 case Resource::Font:
1003 return "Font";
1004 case Resource::Raw:
1005 return "Raw";
1006 case Resource::SVGDocument:
1007 return "SVGDocument";
1008 case Resource::XSLStyleSheet:
1009 return "XSLStyleSheet";
1010 case Resource::LinkPrefetch:
1011 return "LinkPrefetch";
1012 case Resource::LinkSubresource:
1013 return "LinkSubresource";
1014 case Resource::TextTrack:
1015 return "TextTrack";
1016 case Resource::ImportResource:
1017 return "ImportResource";
1018 case Resource::Media:
1019 return "Media";
1020 }
1021 ASSERT_NOT_REACHED();
1022 return "Unknown";
1023 }
1024 #endif // !LOG_DISABLED
1025
1026 }
1027