tizen beta release
[profile/ivi/webkit-efl.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 "MemoryCache.h"
28 #include "CachedMetadata.h"
29 #include "CachedResourceClient.h"
30 #include "CachedResourceClientWalker.h"
31 #include "CachedResourceHandle.h"
32 #include "CachedResourceLoader.h"
33 #include "CrossOriginAccessControl.h"
34 #include "Document.h"
35 #include "Frame.h"
36 #include "FrameLoaderClient.h"
37 #include "KURL.h"
38 #include "Logging.h"
39 #include "PurgeableBuffer.h"
40 #include "ResourceHandle.h"
41 #include "ResourceLoadScheduler.h"
42 #include "SharedBuffer.h"
43 #include "SubresourceLoader.h"
44 #include <wtf/CurrentTime.h>
45 #include <wtf/MathExtras.h>
46 #include <wtf/RefCountedLeakCounter.h>
47 #include <wtf/StdLibExtras.h>
48 #include <wtf/Vector.h>
49
50 using namespace WTF;
51
52 namespace WebCore {
53     
54 static ResourceLoadPriority defaultPriorityForResourceType(CachedResource::Type type)
55 {
56     switch (type) {
57         case CachedResource::CSSStyleSheet:
58 #if ENABLE(XSLT)
59         case CachedResource::XSLStyleSheet:
60 #endif
61             return ResourceLoadPriorityHigh;
62         case CachedResource::Script:
63         case CachedResource::FontResource:
64         case CachedResource::RawResource:
65             return ResourceLoadPriorityMedium;
66         case CachedResource::ImageResource:
67             return ResourceLoadPriorityLow;
68 #if ENABLE(LINK_PREFETCH)
69         case CachedResource::LinkPrefetch:
70             return ResourceLoadPriorityVeryLow;
71         case CachedResource::LinkPrerender:
72             return ResourceLoadPriorityVeryLow;
73         case CachedResource::LinkSubresource:
74             return ResourceLoadPriorityVeryLow;
75 #endif
76 #if ENABLE(VIDEO_TRACK)
77         case CachedResource::TextTrackResource:
78             return ResourceLoadPriorityLow;
79 #endif
80 #if ENABLE(CSS_SHADERS)
81         case CachedResource::ShaderResource:
82             return ResourceLoadPriorityMedium;
83 #endif
84     }
85     ASSERT_NOT_REACHED();
86     return ResourceLoadPriorityLow;
87 }
88
89 #if PLATFORM(CHROMIUM)
90 static ResourceRequest::TargetType cachedResourceTypeToTargetType(CachedResource::Type type)
91 {
92     switch (type) {
93     case CachedResource::CSSStyleSheet:
94 #if ENABLE(XSLT)
95     case CachedResource::XSLStyleSheet:
96 #endif
97         return ResourceRequest::TargetIsStyleSheet;
98     case CachedResource::Script: 
99         return ResourceRequest::TargetIsScript;
100     case CachedResource::FontResource:
101         return ResourceRequest::TargetIsFontResource;
102     case CachedResource::ImageResource:
103         return ResourceRequest::TargetIsImage;
104     case CachedResource::RawResource:
105         return ResourceRequest::TargetIsSubresource;    
106 #if ENABLE(LINK_PREFETCH)
107     case CachedResource::LinkPrefetch:
108         return ResourceRequest::TargetIsPrefetch;
109     case CachedResource::LinkPrerender:
110         return ResourceRequest::TargetIsPrerender;
111     case CachedResource::LinkSubresource:
112         return ResourceRequest::TargetIsSubresource;
113 #endif
114 #if ENABLE(VIDEO_TRACK)
115     case CachedResource::TextTrackResource:
116         return ResourceRequest::TargetIsTextTrack;
117 #endif
118     }
119     ASSERT_NOT_REACHED();
120     return ResourceRequest::TargetIsSubresource;
121 }
122 #endif
123
124 DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("CachedResource"));
125
126 CachedResource::CachedResource(const ResourceRequest& request, Type type)
127     : m_resourceRequest(request)
128     , m_loadPriority(defaultPriorityForResourceType(type))
129     , m_responseTimestamp(currentTime())
130     , m_lastDecodedAccessTime(0)
131     , m_loadFinishTime(0)
132     , m_encodedSize(0)
133     , m_decodedSize(0)
134     , m_accessCount(0)
135     , m_handleCount(0)
136     , m_preloadCount(0)
137     , m_preloadResult(PreloadNotReferenced)
138     , m_inLiveDecodedResourcesList(false)
139     , m_requestedFromNetworkingLayer(false)
140     , m_inCache(false)
141     , m_loading(false)
142     , m_type(type)
143     , m_status(Pending)
144 #ifndef NDEBUG
145     , m_deleted(false)
146     , m_lruIndex(0)
147 #endif
148     , m_nextInAllResourcesList(0)
149     , m_prevInAllResourcesList(0)
150     , m_nextInLiveResourcesList(0)
151     , m_prevInLiveResourcesList(0)
152     , m_owningCachedResourceLoader(0)
153     , m_resourceToRevalidate(0)
154     , m_proxyResource(0)
155 {
156     ASSERT(m_type == unsigned(type)); // m_type is a bitfield, so this tests careless updates of the enum.
157 #ifndef NDEBUG
158     cachedResourceLeakCounter.increment();
159 #endif
160 }
161
162 CachedResource::~CachedResource()
163 {
164     ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this.
165     ASSERT(canDelete());
166     ASSERT(!inCache());
167     ASSERT(!m_deleted);
168     ASSERT(url().isNull() || memoryCache()->resourceForURL(KURL(ParsedURLString, url())) != this);
169     
170 #ifndef NDEBUG
171     m_deleted = true;
172     cachedResourceLeakCounter.decrement();
173 #endif
174
175     if (m_owningCachedResourceLoader)
176         m_owningCachedResourceLoader->removeCachedResource(this);
177 }
178
179 void CachedResource::load(CachedResourceLoader* cachedResourceLoader, const ResourceLoaderOptions& options)
180 {
181     m_options = options;
182     m_loading = true;
183
184 #if PLATFORM(CHROMIUM)
185     if (m_resourceRequest.targetType() == ResourceRequest::TargetIsUnspecified)
186         m_resourceRequest.setTargetType(cachedResourceTypeToTargetType(type()));
187 #endif
188
189     if (!accept().isEmpty())
190         m_resourceRequest.setHTTPAccept(accept());
191
192     if (isCacheValidator()) {
193         CachedResource* resourceToRevalidate = m_resourceToRevalidate;
194         ASSERT(resourceToRevalidate->canUseCacheValidator());
195         ASSERT(resourceToRevalidate->isLoaded());
196         const String& lastModified = resourceToRevalidate->response().httpHeaderField("Last-Modified");
197         const String& eTag = resourceToRevalidate->response().httpHeaderField("ETag");
198         if (!lastModified.isEmpty() || !eTag.isEmpty()) {
199             ASSERT(cachedResourceLoader->cachePolicy() != CachePolicyReload);
200             if (cachedResourceLoader->cachePolicy() == CachePolicyRevalidate)
201                 m_resourceRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
202             if (!lastModified.isEmpty())
203                 m_resourceRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
204             if (!eTag.isEmpty())
205                 m_resourceRequest.setHTTPHeaderField("If-None-Match", eTag);
206         }
207     }
208
209 #if ENABLE(LINK_PREFETCH)
210     if (type() == CachedResource::LinkPrefetch || type() == CachedResource::LinkPrerender || type() == CachedResource::LinkSubresource)
211         m_resourceRequest.setHTTPHeaderField("Purpose", "prefetch");
212 #endif
213     m_resourceRequest.setPriority(loadPriority());
214     
215     m_loader = resourceLoadScheduler()->scheduleSubresourceLoad(cachedResourceLoader->document()->frame(), this, m_resourceRequest, m_resourceRequest.priority(), options);
216     if (!m_loader || m_loader->reachedTerminalState()) {
217         // FIXME: What if resources in other frames were waiting for this revalidation?
218         LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data());
219         if (m_resourceToRevalidate) 
220             memoryCache()->revalidationFailed(this); 
221         error(CachedResource::LoadError);
222         return;
223     }
224
225     m_status = Pending;
226     cachedResourceLoader->incrementRequestCount(this);
227 }
228
229 void CachedResource::checkNotify()
230 {
231     if (isLoading())
232         return;
233
234     CachedResourceClientWalker<CachedResourceClient> w(m_clients);
235     while (CachedResourceClient* c = w.next())
236         c->notifyFinished(this);
237 }
238
239 void CachedResource::data(PassRefPtr<SharedBuffer>, bool allDataReceived)
240 {
241     if (!allDataReceived)
242         return;
243     
244     setLoading(false);
245     checkNotify();
246 }
247
248 void CachedResource::error(CachedResource::Status status)
249 {
250     setStatus(status);
251     ASSERT(errorOccurred());
252     m_data.clear();
253
254     setLoading(false);
255     checkNotify();
256 }
257
258 void CachedResource::finish()
259 {
260     if (!errorOccurred())
261         m_status = Cached;
262 }
263
264 bool CachedResource::passesAccessControlCheck(SecurityOrigin* securityOrigin)
265 {
266     String errorDescription;
267     return WebCore::passesAccessControlCheck(m_response, resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription);
268 }
269
270 bool CachedResource::isExpired() const
271 {
272     if (m_response.isNull())
273         return false;
274
275     return currentAge() > freshnessLifetime();
276 }
277     
278 double CachedResource::currentAge() const
279 {
280     // RFC2616 13.2.3
281     // No compensation for latency as that is not terribly important in practice
282     double dateValue = m_response.date();
283     double apparentAge = isfinite(dateValue) ? max(0., m_responseTimestamp - dateValue) : 0;
284     double ageValue = m_response.age();
285     double correctedReceivedAge = isfinite(ageValue) ? max(apparentAge, ageValue) : apparentAge;
286     double residentTime = currentTime() - m_responseTimestamp;
287     return correctedReceivedAge + residentTime;
288 }
289     
290 double CachedResource::freshnessLifetime() const
291 {
292     // Cache non-http resources liberally
293     if (!m_response.url().protocolInHTTPFamily())
294         return std::numeric_limits<double>::max();
295
296     // RFC2616 13.2.4
297     double maxAgeValue = m_response.cacheControlMaxAge();
298     if (isfinite(maxAgeValue))
299         return maxAgeValue;
300     double expiresValue = m_response.expires();
301     double dateValue = m_response.date();
302     double creationTime = isfinite(dateValue) ? dateValue : m_responseTimestamp;
303     if (isfinite(expiresValue))
304         return expiresValue - creationTime;
305     double lastModifiedValue = m_response.lastModified();
306     if (isfinite(lastModifiedValue))
307         return (creationTime - lastModifiedValue) * 0.1;
308     // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0.
309     return 0;
310 }
311
312 void CachedResource::setResponse(const ResourceResponse& response)
313 {
314     m_response = response;
315     m_responseTimestamp = currentTime();
316     String encoding = response.textEncodingName();
317     if (!encoding.isNull())
318         setEncoding(encoding);
319 }
320
321 void CachedResource::setSerializedCachedMetadata(const char* data, size_t size)
322 {
323     // We only expect to receive cached metadata from the platform once.
324     // If this triggers, it indicates an efficiency problem which is most
325     // likely unexpected in code designed to improve performance.
326     ASSERT(!m_cachedMetadata);
327
328     m_cachedMetadata = CachedMetadata::deserialize(data, size);
329 }
330
331 void CachedResource::setCachedMetadata(unsigned dataTypeID, const char* data, size_t size)
332 {
333     // Currently, only one type of cached metadata per resource is supported.
334     // If the need arises for multiple types of metadata per resource this could
335     // be enhanced to store types of metadata in a map.
336     ASSERT(!m_cachedMetadata);
337
338     m_cachedMetadata = CachedMetadata::create(dataTypeID, data, size);
339     ResourceHandle::cacheMetadata(m_response, m_cachedMetadata->serialize());
340 }
341
342 CachedMetadata* CachedResource::cachedMetadata(unsigned dataTypeID) const
343 {
344     if (!m_cachedMetadata || m_cachedMetadata->dataTypeID() != dataTypeID)
345         return 0;
346     return m_cachedMetadata.get();
347 }
348
349 void CachedResource::stopLoading()
350 {
351     ASSERT(m_loader);            
352     m_loader = 0;
353
354     CachedResourceHandle<CachedResource> protect(this);
355
356     // All loads finish with data(allDataReceived = true) or error(), except for
357     // canceled loads, which silently set our request to 0. Be sure to notify our
358     // client in that case, so we don't seem to continue loading forever.
359     if (isLoading()) {
360         setLoading(false);
361         setStatus(Canceled);
362         checkNotify();
363     }
364 }
365
366 void CachedResource::addClient(CachedResourceClient* client)
367 {
368     addClientToSet(client);
369     didAddClient(client);
370 }
371
372 void CachedResource::didAddClient(CachedResourceClient* c)
373 {
374     if (!isLoading())
375         c->notifyFinished(this);
376 }
377
378 void CachedResource::addClientToSet(CachedResourceClient* client)
379 {
380     ASSERT(!isPurgeable());
381
382     if (m_preloadResult == PreloadNotReferenced) {
383         if (isLoaded())
384             m_preloadResult = PreloadReferencedWhileComplete;
385         else if (m_requestedFromNetworkingLayer)
386             m_preloadResult = PreloadReferencedWhileLoading;
387         else
388             m_preloadResult = PreloadReferenced;
389     }
390     if (!hasClients() && inCache())
391         memoryCache()->addToLiveResourcesSize(this);
392     m_clients.add(client);
393 }
394
395 void CachedResource::removeClient(CachedResourceClient* client)
396 {
397     ASSERT(m_clients.contains(client));
398     m_clients.remove(client);
399
400     if (canDelete() && !inCache())
401         delete this;
402     else if (!hasClients() && inCache()) {
403         memoryCache()->removeFromLiveResourcesSize(this);
404         memoryCache()->removeFromLiveDecodedResourcesList(this);
405         allClientsRemoved();
406         if (response().cacheControlContainsNoStore()) {
407             // RFC2616 14.9.2:
408             // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible"
409             // "... History buffers MAY store such responses as part of their normal operation."
410             // We allow non-secure content to be reused in history, but we do not allow secure content to be reused.
411             if (protocolIs(url(), "https"))
412                 memoryCache()->remove(this);
413         } else
414             memoryCache()->prune();
415     }
416     // This object may be dead here.
417 }
418
419 void CachedResource::deleteIfPossible()
420 {
421     if (canDelete() && !inCache())
422         delete this;
423 }
424     
425 void CachedResource::setDecodedSize(unsigned size)
426 {
427     if (size == m_decodedSize)
428         return;
429
430     int delta = size - m_decodedSize;
431
432     // The object must now be moved to a different queue, since its size has been changed.
433     // We have to remove explicitly before updating m_decodedSize, so that we find the correct previous
434     // queue.
435     if (inCache())
436         memoryCache()->removeFromLRUList(this);
437     
438     m_decodedSize = size;
439    
440     if (inCache()) { 
441         // Now insert into the new LRU list.
442         memoryCache()->insertInLRUList(this);
443         
444         // Insert into or remove from the live decoded list if necessary.
445         // When inserting into the LiveDecodedResourcesList it is possible
446         // that the m_lastDecodedAccessTime is still zero or smaller than
447         // the m_lastDecodedAccessTime of the current list head. This is a
448         // violation of the invariant that the list is to be kept sorted
449         // by access time. The weakening of the invariant does not pose
450         // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209
451         if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients())
452             memoryCache()->insertInLiveDecodedResourcesList(this);
453         else if (!m_decodedSize && m_inLiveDecodedResourcesList)
454             memoryCache()->removeFromLiveDecodedResourcesList(this);
455
456         // Update the cache's size totals.
457         memoryCache()->adjustSize(hasClients(), delta);
458     }
459 }
460
461 void CachedResource::setEncodedSize(unsigned size)
462 {
463     if (size == m_encodedSize)
464         return;
465
466     // The size cannot ever shrink (unless it is being nulled out because of an error).  If it ever does, assert.
467     ASSERT(size == 0 || size >= m_encodedSize);
468     
469     int delta = size - m_encodedSize;
470
471     // The object must now be moved to a different queue, since its size has been changed.
472     // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous
473     // queue.
474     if (inCache())
475         memoryCache()->removeFromLRUList(this);
476     
477     m_encodedSize = size;
478    
479     if (inCache()) { 
480         // Now insert into the new LRU list.
481         memoryCache()->insertInLRUList(this);
482         
483         // Update the cache's size totals.
484         memoryCache()->adjustSize(hasClients(), delta);
485     }
486 }
487
488 void CachedResource::didAccessDecodedData(double timeStamp)
489 {
490     m_lastDecodedAccessTime = timeStamp;
491     
492     if (inCache()) {
493         if (m_inLiveDecodedResourcesList) {
494             memoryCache()->removeFromLiveDecodedResourcesList(this);
495             memoryCache()->insertInLiveDecodedResourcesList(this);
496         }
497         memoryCache()->prune();
498     }
499 }
500     
501 void CachedResource::setResourceToRevalidate(CachedResource* resource) 
502
503     ASSERT(resource);
504     ASSERT(!m_resourceToRevalidate);
505     ASSERT(resource != this);
506     ASSERT(m_handlesToRevalidate.isEmpty());
507     ASSERT(resource->type() == type());
508
509     LOG(ResourceLoading, "CachedResource %p setResourceToRevalidate %p", this, resource);
510
511     // The following assert should be investigated whenever it occurs. Although it should never fire, it currently does in rare circumstances.
512     // https://bugs.webkit.org/show_bug.cgi?id=28604.
513     // So the code needs to be robust to this assert failing thus the "if (m_resourceToRevalidate->m_proxyResource == this)" in CachedResource::clearResourceToRevalidate.
514     ASSERT(!resource->m_proxyResource);
515
516     resource->m_proxyResource = this;
517     m_resourceToRevalidate = resource;
518 }
519
520 void CachedResource::clearResourceToRevalidate() 
521
522     ASSERT(m_resourceToRevalidate);
523     // 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.
524     if (m_resourceToRevalidate->m_proxyResource == this) {
525         m_resourceToRevalidate->m_proxyResource = 0;
526         m_resourceToRevalidate->deleteIfPossible();
527     }
528     m_handlesToRevalidate.clear();
529     m_resourceToRevalidate = 0;
530     deleteIfPossible();
531 }
532     
533 void CachedResource::switchClientsToRevalidatedResource()
534 {
535     ASSERT(m_resourceToRevalidate);
536     ASSERT(m_resourceToRevalidate->inCache());
537     ASSERT(!inCache());
538
539     LOG(ResourceLoading, "CachedResource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate);
540
541     HashSet<CachedResourceHandleBase*>::iterator end = m_handlesToRevalidate.end();
542     for (HashSet<CachedResourceHandleBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) {
543         CachedResourceHandleBase* handle = *it;
544         handle->m_resource = m_resourceToRevalidate;
545         m_resourceToRevalidate->registerHandle(handle);
546         --m_handleCount;
547     }
548     ASSERT(!m_handleCount);
549     m_handlesToRevalidate.clear();
550
551     Vector<CachedResourceClient*> clientsToMove;
552     HashCountedSet<CachedResourceClient*>::iterator end2 = m_clients.end();
553     for (HashCountedSet<CachedResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) {
554         CachedResourceClient* client = it->first;
555         unsigned count = it->second;
556         while (count) {
557             clientsToMove.append(client);
558             --count;
559         }
560     }
561     // Equivalent of calling removeClient() for all clients
562     m_clients.clear();
563
564     unsigned moveCount = clientsToMove.size();
565     for (unsigned n = 0; n < moveCount; ++n)
566         m_resourceToRevalidate->addClientToSet(clientsToMove[n]);
567     for (unsigned n = 0; n < moveCount; ++n) {
568         // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore.
569         if (m_resourceToRevalidate->m_clients.contains(clientsToMove[n]))
570             m_resourceToRevalidate->didAddClient(clientsToMove[n]);
571     }
572 }
573     
574 void CachedResource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse)
575 {
576     m_responseTimestamp = currentTime();
577
578     DEFINE_STATIC_LOCAL(const AtomicString, contentHeaderPrefix, ("content-"));
579     // RFC2616 10.3.5
580     // Update cached headers from the 304 response
581     const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields();
582     HTTPHeaderMap::const_iterator end = newHeaders.end();
583     for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) {
584         // Don't allow 304 response to update content headers, these can't change but some servers send wrong values.
585         if (it->first.startsWith(contentHeaderPrefix, false))
586             continue;
587         m_response.setHTTPHeaderField(it->first, it->second);
588     }
589 }
590
591 void CachedResource::registerHandle(CachedResourceHandleBase* h)
592 {
593     ++m_handleCount;
594     if (m_resourceToRevalidate)
595         m_handlesToRevalidate.add(h);
596 }
597
598 void CachedResource::unregisterHandle(CachedResourceHandleBase* h)
599 {
600     ASSERT(m_handleCount > 0);
601     --m_handleCount;
602
603     if (m_resourceToRevalidate)
604          m_handlesToRevalidate.remove(h);
605
606     if (!m_handleCount)
607         deleteIfPossible();
608 }
609
610 bool CachedResource::canUseCacheValidator() const
611 {
612     if (m_loading || errorOccurred())
613         return false;
614
615     if (m_response.cacheControlContainsNoStore())
616         return false;
617     return m_response.hasCacheValidatorFields();
618 }
619
620 bool CachedResource::mustRevalidateDueToCacheHeaders(CachePolicy cachePolicy) const
621 {    
622     ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyCache || cachePolicy == CachePolicyVerify);
623
624     if (cachePolicy == CachePolicyRevalidate)
625         return true;
626
627     if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) {
628         LOG(ResourceLoading, "CachedResource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this);
629         return true;
630     }
631
632     if (cachePolicy == CachePolicyCache) {
633         if (m_response.cacheControlContainsMustRevalidate() && isExpired()) {
634             LOG(ResourceLoading, "CachedResource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this);
635             return true;
636         }
637         return false;
638     }
639
640     // CachePolicyVerify
641     if (isExpired()) {
642         LOG(ResourceLoading, "CachedResource %p mustRevalidate because of isExpired()\n", this);
643         return true;
644     }
645
646     return false;
647 }
648
649 bool CachedResource::isSafeToMakePurgeable() const
650
651     return !hasClients() && !m_proxyResource && !m_resourceToRevalidate;
652 }
653
654 bool CachedResource::makePurgeable(bool purgeable) 
655
656     if (purgeable) {
657         ASSERT(isSafeToMakePurgeable());
658
659         if (m_purgeableData) {
660             ASSERT(!m_data);
661             return true;
662         }
663         if (!m_data)
664             return false;
665         
666         // Should not make buffer purgeable if it has refs other than this since we don't want two copies.
667         if (!m_data->hasOneRef())
668             return false;
669         
670         if (m_data->hasPurgeableBuffer()) {
671             m_purgeableData = m_data->releasePurgeableBuffer();
672         } else {
673             m_purgeableData = PurgeableBuffer::create(m_data->data(), m_data->size());
674             if (!m_purgeableData)
675                 return false;
676             m_purgeableData->setPurgePriority(purgePriority());
677         }
678         
679         m_purgeableData->makePurgeable(true);
680         m_data.clear();
681         return true;
682     }
683
684     if (!m_purgeableData)
685         return true;
686     ASSERT(!m_data);
687     ASSERT(!hasClients());
688
689     if (!m_purgeableData->makePurgeable(false))
690         return false; 
691
692     m_data = SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release());
693     return true;
694 }
695
696 bool CachedResource::isPurgeable() const
697 {
698     return m_purgeableData && m_purgeableData->isPurgeable();
699 }
700
701 bool CachedResource::wasPurged() const
702 {
703     return m_purgeableData && m_purgeableData->wasPurged();
704 }
705
706 unsigned CachedResource::overheadSize() const
707 {
708     static const int kAverageClientsHashMapSize = 384;
709     return sizeof(CachedResource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2;
710 }
711     
712 void CachedResource::setLoadPriority(ResourceLoadPriority loadPriority) 
713
714     if (loadPriority == ResourceLoadPriorityUnresolved)
715         return;
716     m_loadPriority = loadPriority;
717 }
718
719 }