[iOS] Upstream WebCore/loader changes
[WebKit-https.git] / Source / WebCore / loader / cache / CachedResource.cpp
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 "CachedResource.h"
26
27 #include "CachedResourceClient.h"
28 #include "CachedResourceClientWalker.h"
29 #include "CachedResourceHandle.h"
30 #include "CachedResourceLoader.h"
31 #include "CrossOriginAccessControl.h"
32 #include "Document.h"
33 #include "DocumentLoader.h"
34 #include "FrameLoader.h"
35 #include "FrameLoaderClient.h"
36 #include "InspectorInstrumentation.h"
37 #include "URL.h"
38 #include "LoaderStrategy.h"
39 #include "Logging.h"
40 #include "MemoryCache.h"
41 #include "PlatformStrategies.h"
42 #include "PurgeableBuffer.h"
43 #include "ResourceBuffer.h"
44 #include "ResourceHandle.h"
45 #include "ResourceLoadScheduler.h"
46 #include "SchemeRegistry.h"
47 #include "SecurityOrigin.h"
48 #include "SecurityPolicy.h"
49 #include "SubresourceLoader.h"
50 #include <wtf/CurrentTime.h>
51 #include <wtf/MathExtras.h>
52 #include <wtf/RefCountedLeakCounter.h>
53 #include <wtf/StdLibExtras.h>
54 #include <wtf/text/CString.h>
55 #include <wtf/Vector.h>
56
57 #if USE(QUICK_LOOK)
58 #include "QuickLook.h"
59 #endif
60
61 using namespace WTF;
62
63 namespace WebCore {
64
65 // These response headers are not copied from a revalidated response to the
66 // cached response headers. For compatibility, this list is based on Chromium's
67 // net/http/http_response_headers.cc.
68 const char* const headersToIgnoreAfterRevalidation[] = {
69     "allow",
70     "connection",
71     "etag",
72     "expires",
73     "keep-alive",
74     "last-modified"
75     "proxy-authenticate",
76     "proxy-connection",
77     "trailer",
78     "transfer-encoding",
79     "upgrade",
80     "www-authenticate",
81     "x-frame-options",
82     "x-xss-protection",
83 };
84
85 // Some header prefixes mean "Don't copy this header from a 304 response.".
86 // Rather than listing all the relevant headers, we can consolidate them into
87 // this list, also grabbed from Chromium's net/http/http_response_headers.cc.
88 const char* const headerPrefixesToIgnoreAfterRevalidation[] = {
89     "content-",
90     "x-content-",
91     "x-webkit-"
92 };
93
94 static inline bool shouldUpdateHeaderAfterRevalidation(const AtomicString& header)
95 {
96     for (size_t i = 0; i < WTF_ARRAY_LENGTH(headersToIgnoreAfterRevalidation); i++) {
97         if (equalIgnoringCase(header, headersToIgnoreAfterRevalidation[i]))
98             return false;
99     }
100     for (size_t i = 0; i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) {
101         if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i], false))
102             return false;
103     }
104     return true;
105 }
106
107 static ResourceLoadPriority defaultPriorityForResourceType(CachedResource::Type type)
108 {
109     switch (type) {
110     case CachedResource::MainResource:
111         return ResourceLoadPriorityVeryHigh;
112     case CachedResource::CSSStyleSheet:
113         return ResourceLoadPriorityHigh;
114     case CachedResource::Script:
115     case CachedResource::FontResource:
116     case CachedResource::RawResource:
117         return ResourceLoadPriorityMedium;
118     case CachedResource::ImageResource:
119         return ResourceLoadPriorityLow;
120 #if ENABLE(XSLT)
121     case CachedResource::XSLStyleSheet:
122         return ResourceLoadPriorityHigh;
123 #endif
124 #if ENABLE(SVG)
125     case CachedResource::SVGDocumentResource:
126         return ResourceLoadPriorityLow;
127 #endif
128 #if ENABLE(LINK_PREFETCH)
129     case CachedResource::LinkPrefetch:
130         return ResourceLoadPriorityVeryLow;
131     case CachedResource::LinkSubresource:
132         return ResourceLoadPriorityVeryLow;
133 #endif
134 #if ENABLE(VIDEO_TRACK)
135     case CachedResource::TextTrackResource:
136         return ResourceLoadPriorityLow;
137 #endif
138 #if ENABLE(CSS_SHADERS)
139     case CachedResource::ShaderResource:
140         return ResourceLoadPriorityMedium;
141 #endif
142     }
143     ASSERT_NOT_REACHED();
144     return ResourceLoadPriorityLow;
145 }
146
147 #if PLATFORM(BLACKBERRY)
148 static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type)
149 {
150     switch (type) {
151     case CachedResource::MainResource:
152         return ResourceRequest::TargetIsMainFrame;
153     case CachedResource::CSSStyleSheet:
154 #if ENABLE(XSLT)
155     case CachedResource::XSLStyleSheet:
156 #endif
157         return ResourceRequest::TargetIsStyleSheet;
158     case CachedResource::Script: 
159         return ResourceRequest::TargetIsScript;
160     case CachedResource::FontResource:
161         return ResourceRequest::TargetIsFontResource;
162     case CachedResource::ImageResource:
163         return ResourceRequest::TargetIsImage;
164 #if ENABLE(CSS_SHADERS)
165     case CachedResource::ShaderResource:
166 #endif
167     case CachedResource::RawResource:
168         return ResourceRequest::TargetIsSubresource;    
169 #if ENABLE(LINK_PREFETCH)
170     case CachedResource::LinkPrefetch:
171         return ResourceRequest::TargetIsPrefetch;
172     case CachedResource::LinkSubresource:
173         return ResourceRequest::TargetIsSubresource;
174 #endif
175 #if ENABLE(VIDEO_TRACK)
176     case CachedResource::TextTrackResource:
177         return ResourceRequest::TargetIsTextTrack;
178 #endif
179 #if ENABLE(SVG)
180     case CachedResource::SVGDocumentResource:
181         return ResourceRequest::TargetIsImage;
182 #endif
183     }
184     ASSERT_NOT_REACHED();
185     return ResourceRequest::TargetIsSubresource;
186 }
187 #endif
188
189 static double deadDecodedDataDeletionIntervalForResourceType(CachedResource::Type type)
190 {
191     if (type == CachedResource::Script)
192         return 0;
193     return memoryCache()->deadDecodedDataDeletionInterval();
194 }
195
196 DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("CachedResource"));
197
198 CachedResource::CachedResource(const ResourceRequest& request, Type type)
199     : m_resourceRequest(request)
200     , m_loadPriority(defaultPriorityForResourceType(type))
201     , m_responseTimestamp(currentTime())
202     , m_decodedDataDeletionTimer(this, &CachedResource::decodedDataDeletionTimerFired, deadDecodedDataDeletionIntervalForResourceType(type))
203     , m_lastDecodedAccessTime(0)
204     , m_loadFinishTime(0)
205     , m_encodedSize(0)
206     , m_decodedSize(0)
207     , m_accessCount(0)
208     , m_handleCount(0)
209     , m_preloadCount(0)
210     , m_preloadResult(PreloadNotReferenced)
211     , m_inLiveDecodedResourcesList(false)
212     , m_requestedFromNetworkingLayer(false)
213     , m_inCache(false)
214     , m_loading(false)
215     , m_switchingClientsToRevalidatedResource(false)
216     , m_type(type)
217     , m_status(Pending)
218 #ifndef NDEBUG
219     , m_deleted(false)
220     , m_lruIndex(0)
221 #endif
222     , m_nextInAllResourcesList(0)
223     , m_prevInAllResourcesList(0)
224     , m_nextInLiveResourcesList(0)
225     , m_prevInLiveResourcesList(0)
226     , m_owningCachedResourceLoader(0)
227     , m_resourceToRevalidate(0)
228     , m_proxyResource(0)
229 {
230     ASSERT(m_type == unsigned(type)); // m_type is a bitfield, so this tests careless updates of the enum.
231 #ifndef NDEBUG
232     cachedResourceLeakCounter.increment();
233 #endif
234
235     if (!m_resourceRequest.url().hasFragmentIdentifier())
236         return;
237     URL urlForCache = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url());
238     if (urlForCache.hasFragmentIdentifier())
239         return;
240     m_fragmentIdentifierForRequest = m_resourceRequest.url().fragmentIdentifier();
241     m_resourceRequest.setURL(urlForCache);
242 }
243
244 CachedResource::~CachedResource()
245 {
246     ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this.
247     ASSERT(canDelete());
248     ASSERT(!inCache());
249     ASSERT(!m_deleted);
250     ASSERT(url().isNull() || memoryCache()->resourceForRequest(resourceRequest()) != this);
251
252 #ifndef NDEBUG
253     m_deleted = true;
254     cachedResourceLeakCounter.decrement();
255 #endif
256
257     if (m_owningCachedResourceLoader)
258         m_owningCachedResourceLoader->removeCachedResource(this);
259 }
260
261 void CachedResource::failBeforeStarting()
262 {
263     // FIXME: What if resources in other frames were waiting for this revalidation?
264     LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data());
265     if (m_resourceToRevalidate) 
266         memoryCache()->revalidationFailed(this); 
267     error(CachedResource::LoadError);
268 }
269
270 void CachedResource::addAdditionalRequestHeaders(CachedResourceLoader* cachedResourceLoader)
271 {
272     // Note: We skip the Content-Security-Policy check here because we check
273     // the Content-Security-Policy at the CachedResourceLoader layer so we can
274     // handle different resource types differently.
275
276     FrameLoader& frameLoader = cachedResourceLoader->frame()->loader();
277     String outgoingReferrer;
278     String outgoingOrigin;
279     if (m_resourceRequest.httpReferrer().isNull()) {
280         outgoingReferrer = frameLoader.outgoingReferrer();
281         outgoingOrigin = frameLoader.outgoingOrigin();
282     } else {
283         outgoingReferrer = m_resourceRequest.httpReferrer();
284         outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
285     }
286
287     outgoingReferrer = SecurityPolicy::generateReferrerHeader(cachedResourceLoader->document()->referrerPolicy(), m_resourceRequest.url(), outgoingReferrer);
288     if (outgoingReferrer.isEmpty())
289         m_resourceRequest.clearHTTPReferrer();
290     else if (!m_resourceRequest.httpReferrer())
291         m_resourceRequest.setHTTPReferrer(outgoingReferrer);
292     FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin);
293
294     frameLoader.addExtraFieldsToSubresourceRequest(m_resourceRequest);
295 }
296
297 void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
298 {
299     if (!cachedResourceLoader->frame()) {
300         failBeforeStarting();
301         return;
302     }
303
304     FrameLoader& frameLoader = cachedResourceLoader->frame()->loader();
305     if (options.securityCheck == DoSecurityCheck && (frameLoader.state() == FrameStateProvisional || !frameLoader.activeDocumentLoader() || frameLoader.activeDocumentLoader()->isStopping())) {
306         failBeforeStarting();
307         return;
308     }
309
310     m_options = options;
311     m_loading = true;
312
313 #if USE(QUICK_LOOK)
314     if (!m_resourceRequest.isNull() && m_resourceRequest.url().protocolIs(QLPreviewProtocol())) {
315         // When QuickLook is invoked to convert a document, it returns a unique URL in the
316         // NSURLReponse for the main document. To make safeQLURLForDocumentURLAndResourceURL()
317         // work, we need to use the QL URL not the original URL.
318         const URL& documentURL = cachedResourceLoader->frame() ? cachedResourceLoader->frame()->loader().documentLoader()->response().url() : cachedResourceLoader->document()->url();
319         m_resourceRequest.setURL(safeQLURLForDocumentURLAndResourceURL(documentURL, url()));
320     }
321 #endif
322
323 #if PLATFORM(BLACKBERRY)
324     if (m_resourceRequest.targetType() == ResourceRequest::TargetIsUnspecified)
325         m_resourceRequest.setTargetType(cachedResourceTypeToTargetType(type()));
326 #endif
327
328     if (!accept().isEmpty())
329         m_resourceRequest.setHTTPAccept(accept());
330
331     if (isCacheValidator()) {
332         CachedResource* resourceToRevalidate = m_resourceToRevalidate;
333         ASSERT(resourceToRevalidate->canUseCacheValidator());
334         ASSERT(resourceToRevalidate->isLoaded());
335         const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified");
336         const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag");
337         if (!lastModified.isEmpty() || !eTag.isEmpty()) {
338             ASSERT(cachedResourceLoader->cachePolicy(type()) != CachePolicyReload);
339             if (cachedResourceLoader->cachePolicy(type()) == CachePolicyRevalidate)
340                 m_resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
341             if (!lastModified.isEmpty())
342                 m_resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
343             if (!eTag.isEmpty())
344                 m_resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
345         }
346     }
347
348 #if ENABLE(LINK_PREFETCH)
349     if (type() == CachedResource::LinkPrefetch || type() == CachedResource::LinkSubresource)
350         m_resourceRequest.setHTTPHeaderField("Purpose", "prefetch");
351 #endif
352     m_resourceRequest.setPriority(loadPriority());
353
354     if (type() != MainResource)
355         addAdditionalRequestHeaders(cachedResourceLoader);
356
357     // FIXME: It's unfortunate that the cache layer and below get to know anything about fragment identifiers.
358     // We should look into removing the expectation of that knowledge from the platform network stacks.
359     ResourceRequest request(m_resourceRequest);
360     if (!m_fragmentIdentifierForRequest.isNull()) {
361         URL url = request.url();
362         url.setFragmentIdentifier(m_fragmentIdentifierForRequest);
363         request.setURL(url);
364         m_fragmentIdentifierForRequest = String();
365     }
366
367     m_loader = platformStrategies()->loaderStrategy()->resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->frame(), this, request, request.priority(), options);
368     if (!m_loader) {
369         failBeforeStarting();
370         return;
371     }
372
373     m_status = Pending;
374 }
375
376 void CachedResource::checkNotify()
377 {
378     if (isLoading())
379         return;
380
381     CachedResourceClientWalker<CachedResourceClient> w(m_clients);
382     while (CachedResourceClient* c = w.next())
383         c->notifyFinished(this);
384 }
385
386 void CachedResource::addDataBuffer(ResourceBuffer*)
387 {
388     ASSERT(m_options.dataBufferingPolicy == BufferData);
389 }
390
391 void CachedResource::addData(const char*, unsigned)
392 {
393     ASSERT(m_options.dataBufferingPolicy == DoNotBufferData);
394 }
395
396 void CachedResource::finishLoading(ResourceBuffer*)
397 {
398     setLoading(false);
399     checkNotify();
400 }
401
402 void CachedResource::error(CachedResource::Status status)
403 {
404     setStatus(status);
405     ASSERT(errorOccurred());
406     m_data.clear();
407
408     setLoading(false);
409     checkNotify();
410 }
411     
412 void CachedResource::cancelLoad()
413 {
414     if (!isLoading())
415         return;
416
417     setStatus(LoadError);
418     setLoading(false);
419     checkNotify();
420 }
421
422 void CachedResource::finish()
423 {
424     if (!errorOccurred())
425         m_status = Cached;
426 }
427
428 bool CachedResource::passesAccessControlCheck(SecurityOrigin* securityOrigin)
429 {
430     String errorDescription;
431     return WebCore::passesAccessControlCheck(m_response, resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription);
432 }
433
434 bool CachedResource::isExpired() const
435 {
436     if (m_response.isNull())
437         return false;
438
439     return currentAge() > freshnessLifetime();
440 }
441
442 double CachedResource::currentAge() const
443 {
444     // RFC2616 13.2.3
445     // No compensation for latency as that is not terribly important in practice
446     double dateValue = m_response.date();
447     double apparentAge = std::isfinite(dateValue) ? std::max(0., m_responseTimestamp - dateValue) : 0;
448     double ageValue = m_response.age();
449     double correctedReceivedAge = std::isfinite(ageValue) ? std::max(apparentAge, ageValue) : apparentAge;
450     double residentTime = currentTime() - m_responseTimestamp;
451     return correctedReceivedAge + residentTime;
452 }
453
454 double CachedResource::freshnessLifetime() const
455 {
456     if (!m_response.url().protocolIsInHTTPFamily()) {
457         // Don't cache non-HTTP main resources since we can't check for freshness.
458         // FIXME: We should not cache subresources either, but when we tried this
459         // it caused performance and flakiness issues in our test infrastructure.
460         if (m_type == MainResource && !SchemeRegistry::shouldCacheResponsesFromURLSchemeIndefinitely(m_response.url().protocol()))
461             return 0;
462
463         return std::numeric_limits<double>::max();
464     }
465
466     // RFC2616 13.2.4
467     double maxAgeValue = m_response.cacheControlMaxAge();
468     if (std::isfinite(maxAgeValue))
469         return maxAgeValue;
470     double expiresValue = m_response.expires();
471     double dateValue = m_response.date();
472     double creationTime = std::isfinite(dateValue) ? dateValue : m_responseTimestamp;
473     if (std::isfinite(expiresValue))
474         return expiresValue - creationTime;
475     double lastModifiedValue = m_response.lastModified();
476     if (std::isfinite(lastModifiedValue))
477         return (creationTime - lastModifiedValue) * 0.1;
478     // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0.
479     return 0;
480 }
481
482 void CachedResource::responseReceived(const ResourceResponse& response)
483 {
484     setResponse(response);
485     m_responseTimestamp = currentTime();
486     String encoding = response.textEncodingName();
487     if (!encoding.isNull())
488         setEncoding(encoding);
489 }
490
491 void CachedResource::clearLoader()
492 {
493     ASSERT(m_loader);
494     m_loader = 0;
495 }
496
497 void CachedResource::addClient(CachedResourceClient* client)
498 {
499     if (addClientToSet(client))
500         didAddClient(client);
501 }
502
503 void CachedResource::didAddClient(CachedResourceClient* c)
504 {
505     if (m_decodedDataDeletionTimer.isActive())
506         m_decodedDataDeletionTimer.stop();
507
508     if (m_clientsAwaitingCallback.contains(c)) {
509         m_clients.add(c);
510         m_clientsAwaitingCallback.remove(c);
511     }
512     if (!isLoading() && !stillNeedsLoad())
513         c->notifyFinished(this);
514 }
515
516 bool CachedResource::addClientToSet(CachedResourceClient* client)
517 {
518     ASSERT(!isPurgeable());
519
520     if (m_preloadResult == PreloadNotReferenced) {
521         if (isLoaded())
522             m_preloadResult = PreloadReferencedWhileComplete;
523         else if (m_requestedFromNetworkingLayer)
524             m_preloadResult = PreloadReferencedWhileLoading;
525         else
526             m_preloadResult = PreloadReferenced;
527     }
528     if (!hasClients() && inCache())
529         memoryCache()->addToLiveResourcesSize(this);
530
531     if ((m_type == RawResource || m_type == MainResource) && !m_response.isNull() && !m_proxyResource) {
532         // Certain resources (especially XHRs and main resources) do crazy things if an asynchronous load returns
533         // synchronously (e.g., scripts may not have set all the state they need to handle the load).
534         // Therefore, rather than immediately sending callbacks on a cache hit like other CachedResources,
535         // we schedule the callbacks and ensure we never finish synchronously.
536         ASSERT(!m_clientsAwaitingCallback.contains(client));
537         m_clientsAwaitingCallback.add(client, CachedResourceCallback::schedule(this, client));
538         return false;
539     }
540
541     m_clients.add(client);
542     return true;
543 }
544
545 void CachedResource::removeClient(CachedResourceClient* client)
546 {
547     OwnPtr<CachedResourceCallback> callback = m_clientsAwaitingCallback.take(client);
548     if (callback) {
549         ASSERT(!m_clients.contains(client));
550         callback->cancel();
551         callback.clear();
552     } else {
553         ASSERT(m_clients.contains(client));
554         m_clients.remove(client);
555         didRemoveClient(client);
556     }
557
558     bool deleted = deleteIfPossible();
559     if (!deleted && !hasClients()) {
560         if (inCache()) {
561             memoryCache()->removeFromLiveResourcesSize(this);
562             memoryCache()->removeFromLiveDecodedResourcesList(this);
563         }
564         if (!m_switchingClientsToRevalidatedResource)
565             allClientsRemoved();
566         destroyDecodedDataIfNeeded();
567         if (response().cacheControlContainsNoStore()) {
568             // RFC2616 14.9.2:
569             // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
570             // "... History buffers MAY store such responses as part of their normal operation."
571             // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
572             if (url().protocolIs("https"))
573                 memoryCache()->remove(this);
574         } else
575             memoryCache()->prune();
576     }
577     // This object may be dead here.
578 }
579
580 void CachedResource::destroyDecodedDataIfNeeded()
581 {
582     if (!m_decodedSize)
583         return;
584     if (!memoryCache()->deadDecodedDataDeletionInterval())
585         return;
586     m_decodedDataDeletionTimer.restart();
587 }
588
589 void CachedResource::decodedDataDeletionTimerFired(DeferrableOneShotTimer<CachedResource>*)
590 {
591     destroyDecodedData();
592 }
593
594 bool CachedResource::deleteIfPossible()
595 {
596     if (canDelete() && !inCache()) {
597         InspectorInstrumentation::willDestroyCachedResource(this);
598         delete this;
599         return true;
600     }
601     return false;
602 }
603
604 void CachedResource::setDecodedSize(unsigned size)
605 {
606     if (size == m_decodedSize)
607         return;
608
609     int delta = size - m_decodedSize;
610
611     // The object must now be moved to a different queue, since its size has been changed.
612     // We have to remove explicitly before updating m_decodedSize, so that we find the correct previous
613     // queue.
614     if (inCache())
615         memoryCache()->removeFromLRUList(this);
616     
617     m_decodedSize = size;
618    
619     if (inCache()) { 
620         // Now insert into the new LRU list.
621         memoryCache()->insertInLRUList(this);
622         
623         // Insert into or remove from the live decoded list if necessary.
624         // When inserting into the LiveDecodedResourcesList it is possible
625         // that the m_lastDecodedAccessTime is still zero or smaller than
626         // the m_lastDecodedAccessTime of the current list head. This is a
627         // violation of the invariant that the list is to be kept sorted
628         // by access time. The weakening of the invariant does not pose
629         // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209
630         if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients())
631             memoryCache()->insertInLiveDecodedResourcesList(this);
632         else if (!m_decodedSize && m_inLiveDecodedResourcesList)
633             memoryCache()->removeFromLiveDecodedResourcesList(this);
634
635         // Update the cache's size totals.
636         memoryCache()->adjustSize(hasClients(), delta);
637     }
638 }
639
640 void CachedResource::setEncodedSize(unsigned size)
641 {
642     if (size == m_encodedSize)
643         return;
644
645     // The size cannot ever shrink (unless it is being nulled out because of an error).  If it ever does, assert.
646     ASSERT(size == 0 || size >= m_encodedSize);
647     
648     int delta = size - m_encodedSize;
649
650     // The object must now be moved to a different queue, since its size has been changed.
651     // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous
652     // queue.
653     if (inCache())
654         memoryCache()->removeFromLRUList(this);
655     
656     m_encodedSize = size;
657    
658     if (inCache()) { 
659         // Now insert into the new LRU list.
660         memoryCache()->insertInLRUList(this);
661         
662         // Update the cache's size totals.
663         memoryCache()->adjustSize(hasClients(), delta);
664     }
665 }
666
667 void CachedResource::didAccessDecodedData(double timeStamp)
668 {
669     m_lastDecodedAccessTime = timeStamp;
670     
671     if (inCache()) {
672         if (m_inLiveDecodedResourcesList) {
673             memoryCache()->removeFromLiveDecodedResourcesList(this);
674             memoryCache()->insertInLiveDecodedResourcesList(this);
675         }
676         memoryCache()->prune();
677     }
678 }
679     
680 void CachedResource::setResourceToRevalidate(CachedResource* resource) 
681
682     ASSERT(resource);
683     ASSERT(!m_resourceToRevalidate);
684     ASSERT(resource != this);
685     ASSERT(m_handlesToRevalidate.isEmpty());
686     ASSERT(resource->type() == type());
687
688     LOG(ResourceLoading, "CachedResource %p setResourceToRevalidate %p", this, resource);
689
690     // The following assert should be investigated whenever it occurs. Although it should never fire, it currently does in rare circumstances.
691     // https://bugs.webkit.org/show_bug.cgi?id=28604.
692     // So the code needs to be robust to this assert failing thus the "if (m_resourceToRevalidate->m_proxyResource == this)" in CachedResource::clearResourceToRevalidate.
693     ASSERT(!resource->m_proxyResource);
694
695     resource->m_proxyResource = this;
696     m_resourceToRevalidate = resource;
697 }
698
699 void CachedResource::clearResourceToRevalidate() 
700
701     ASSERT(m_resourceToRevalidate);
702     if (m_switchingClientsToRevalidatedResource)
703         return;
704
705     // 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.
706     if (m_resourceToRevalidate->m_proxyResource == this) {
707         m_resourceToRevalidate->m_proxyResource = 0;
708         m_resourceToRevalidate->deleteIfPossible();
709     }
710     m_handlesToRevalidate.clear();
711     m_resourceToRevalidate = 0;
712     deleteIfPossible();
713 }
714     
715 void CachedResource::switchClientsToRevalidatedResource()
716 {
717     ASSERT(m_resourceToRevalidate);
718     ASSERT(m_resourceToRevalidate->inCache());
719     ASSERT(!inCache());
720
721     LOG(ResourceLoading, "CachedResource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate);
722
723     m_switchingClientsToRevalidatedResource = true;
724     HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end();
725     for (HashSet<CachedResourceHandleBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) {
726         CachedResourceHandleBase* handle = *it;
727         handle->m_resource = m_resourceToRevalidate;
728         m_resourceToRevalidate->registerHandle(handle);
729         --m_handleCount;
730     }
731     ASSERT(!m_handleCount);
732     m_handlesToRevalidate.clear();
733
734     Vector<CachedResourceClient*> clientsToMove;
735     HashCountedSet<CachedResourceClient*>::iterator end2 = m_clients.end();
736     for (HashCountedSet<CachedResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) {
737         CachedResourceClient* client = it->key;
738         unsigned count = it->value;
739         while (count) {
740             clientsToMove.append(client);
741             --count;
742         }
743     }
744
745     unsigned moveCount = clientsToMove.size();
746     for (unsigned n = 0; n < moveCount; ++n)
747         removeClient(clientsToMove[n]);
748     ASSERT(m_clients.isEmpty());
749
750     for (unsigned n = 0; n < moveCount; ++n)
751         m_resourceToRevalidate->addClientToSet(clientsToMove[n]);
752     for (unsigned n = 0; n < moveCount; ++n) {
753         // Calling didAddClient may do anything, including trying to cancel revalidation.
754         // Assert that it didn't succeed.
755         ASSERT(m_resourceToRevalidate);
756         // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore.
757         if (m_resourceToRevalidate->m_clients.contains(clientsToMove[n]))
758             m_resourceToRevalidate->didAddClient(clientsToMove[n]);
759     }
760     m_switchingClientsToRevalidatedResource = false;
761 }
762
763 void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse)
764 {
765     m_responseTimestamp = currentTime();
766
767     // RFC2616 10.3.5
768     // Update cached headers from the 304 response
769     const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields();
770     HTTPHeaderMap::const_iterator end = newHeaders.end();
771     for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) {
772         // Entity headers should not be sent by servers when generating a 304
773         // response; misconfigured servers send them anyway. We shouldn't allow
774         // such headers to update the original request. We'll base this on the
775         // list defined by RFC2616 7.1, with a few additions for extension headers
776         // we care about.
777         if (!shouldUpdateHeaderAfterRevalidation(it->key))
778             continue;
779         m_response.setHTTPHeaderField(it->key, it->value);
780     }
781 }
782
783 void CachedResource::registerHandle(CachedResourceHandleBase* h)
784 {
785     ++m_handleCount;
786     if (m_resourceToRevalidate)
787         m_handlesToRevalidate.add(h);
788 }
789
790 void CachedResource::unregisterHandle(CachedResourceHandleBase* h)
791 {
792     ASSERT(m_handleCount > 0);
793     --m_handleCount;
794
795     if (m_resourceToRevalidate)
796          m_handlesToRevalidate.remove(h);
797
798     if (!m_handleCount)
799         deleteIfPossible();
800 }
801
802 bool CachedResource::canUseCacheValidator() const
803 {
804     if (m_loading || errorOccurred())
805         return false;
806
807     if (m_response.cacheControlContainsNoStore())
808         return false;
809     return m_response.hasCacheValidatorFields();
810 }
811
812 bool CachedResource::mustRevalidateDueToCacheHeaders(CachePolicy cachePolicy) const
813 {    
814     ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyCache || cachePolicy == CachePolicyVerify);
815
816     if (cachePolicy == CachePolicyRevalidate)
817         return true;
818
819     if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) {
820         LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this);
821         return true;
822     }
823
824     if (cachePolicy == CachePolicyCache) {
825         if (m_response.cacheControlContainsMustRevalidate() && isExpired()) {
826             LOG(ResourceLoading, "CachedResource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this);
827             return true;
828         }
829         return false;
830     }
831
832     // CachePolicyVerify
833     if (isExpired()) {
834         LOG(ResourceLoading, "CachedResource %p mustRevalidate because of isExpired()\n", this);
835         return true;
836     }
837
838     return false;
839 }
840
841 bool CachedResource::isSafeToMakePurgeable() const
842
843 #if ENABLE(DISK_IMAGE_CACHE)
844     // It does not make sense to have a resource in the disk image cache
845     // (memory mapped on disk) and purgeable (in memory). So do not allow
846     // disk image cached resources to be purgeable.
847     if (isUsingDiskImageCache())
848         return false;
849 #endif
850
851     return !hasClients() && !m_proxyResource && !m_resourceToRevalidate;
852 }
853
854 bool CachedResource::makePurgeable(bool purgeable) 
855
856     if (purgeable) {
857         ASSERT(isSafeToMakePurgeable());
858
859         if (m_purgeableData) {
860             ASSERT(!m_data);
861             return true;
862         }
863         if (!m_data)
864             return false;
865         
866         // Should not make buffer purgeable if it has refs other than this since we don't want two copies.
867         if (!m_data->hasOneRef())
868             return false;
869
870         m_data->createPurgeableBuffer();
871         if (!m_data->hasPurgeableBuffer())
872             return false;
873
874         m_purgeableData = m_data->releasePurgeableBuffer();
875         m_purgeableData->setPurgePriority(purgePriority());
876         m_purgeableData->makePurgeable(true);
877         m_data.clear();
878         return true;
879     }
880
881     if (!m_purgeableData)
882         return true;
883     ASSERT(!m_data);
884     ASSERT(!hasClients());
885
886     if (!m_purgeableData->makePurgeable(false))
887         return false; 
888
889     m_data = ResourceBuffer::adoptSharedBuffer(SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release()));
890     return true;
891 }
892
893 bool CachedResource::isPurgeable() const
894 {
895     return m_purgeableData && m_purgeableData->isPurgeable();
896 }
897
898 bool CachedResource::wasPurged() const
899 {
900     return m_purgeableData && m_purgeableData->wasPurged();
901 }
902
903 unsigned CachedResource::overheadSize() const
904 {
905     static const int kAverageClientsHashMapSize = 384;
906     return sizeof(CachedResource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2;
907 }
908
909 void CachedResource::setLoadPriority(ResourceLoadPriority loadPriority)
910 {
911     if (loadPriority == ResourceLoadPriorityUnresolved)
912         loadPriority = defaultPriorityForResourceType(type());
913     if (loadPriority == m_loadPriority)
914         return;
915     m_loadPriority = loadPriority;
916     if (m_loader)
917         m_loader->didChangePriority(loadPriority);
918 }
919
920 CachedResource::CachedResourceCallback::CachedResourceCallback(CachedResource* resource, CachedResourceClient* client)
921     : m_resource(resource)
922     , m_client(client)
923     , m_callbackTimer(this, &CachedResourceCallback::timerFired)
924 {
925     m_callbackTimer.startOneShot(0);
926 }
927
928 void CachedResource::CachedResourceCallback::cancel()
929 {
930     if (m_callbackTimer.isActive())
931         m_callbackTimer.stop();
932 }
933
934 void CachedResource::CachedResourceCallback::timerFired(Timer<CachedResourceCallback>*)
935 {
936     m_resource->didAddClient(m_client);
937 }
938
939 #if ENABLE(DISK_IMAGE_CACHE)
940 bool CachedResource::isUsingDiskImageCache() const
941 {
942     return m_data && m_data->isUsingDiskImageCache();
943 }
944 #endif
945
946 #if PLATFORM(MAC)
947 void CachedResource::tryReplaceEncodedData(PassRefPtr<SharedBuffer> newBuffer)
948 {
949     if (!m_data)
950         return;
951     
952     if (!mayTryReplaceEncodedData())
953         return;
954     
955     // Because the disk cache is asynchronous and racey with regards to the data we might be asked to replace,
956     // we need to verify that the new buffer has the same contents as our old buffer.
957     if (m_data->size() != newBuffer->size() || memcmp(m_data->data(), newBuffer->data(), m_data->size()))
958         return;
959
960     m_data->tryReplaceSharedBufferContents(newBuffer.get());
961 }
962 #endif
963
964 }