• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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