1 /* 2 Copyright (C) 1998 Lars Knoll (knoll (at) mpi-hd.mpg.de) 3 Copyright (C) 2001 Dirk Mueller (mueller (at) kde.org) 4 Copyright (C) 2002 Waldo Bastian (bastian (at) kde.org) 5 Copyright (C) 2006 Samuel Weinig (sam.weinig (at) 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/loader/cache/Resource.h" 26 27 #include "core/inspector/InspectorInstrumentation.h" 28 #include "core/loader/CachedMetadata.h" 29 #include "core/loader/CrossOriginAccessControl.h" 30 #include "core/loader/ResourceLoader.h" 31 #include "core/loader/cache/MemoryCache.h" 32 #include "core/loader/cache/ResourceClient.h" 33 #include "core/loader/cache/ResourceClientWalker.h" 34 #include "core/loader/cache/ResourceFetcher.h" 35 #include "core/loader/cache/ResourcePtr.h" 36 #include "core/platform/Logging.h" 37 #include "core/platform/PurgeableBuffer.h" 38 #include "core/platform/SharedBuffer.h" 39 #include "public/platform/Platform.h" 40 #include "weborigin/KURL.h" 41 #include "wtf/CurrentTime.h" 42 #include "wtf/MathExtras.h" 43 #include "wtf/RefCountedLeakCounter.h" 44 #include "wtf/StdLibExtras.h" 45 #include "wtf/Vector.h" 46 #include "wtf/text/CString.h" 47 48 using namespace WTF; 49 50 namespace WebCore { 51 52 // These response headers are not copied from a revalidated response to the 53 // cached response headers. For compatibility, this list is based on Chromium's 54 // net/http/http_response_headers.cc. 55 const char* const headersToIgnoreAfterRevalidation[] = { 56 "allow", 57 "connection", 58 "etag", 59 "expires", 60 "keep-alive", 61 "last-modified" 62 "proxy-authenticate", 63 "proxy-connection", 64 "trailer", 65 "transfer-encoding", 66 "upgrade", 67 "www-authenticate", 68 "x-frame-options", 69 "x-xss-protection", 70 }; 71 72 // Some header prefixes mean "Don't copy this header from a 304 response.". 73 // Rather than listing all the relevant headers, we can consolidate them into 74 // this list, also grabbed from Chromium's net/http/http_response_headers.cc. 75 const char* const headerPrefixesToIgnoreAfterRevalidation[] = { 76 "content-", 77 "x-content-", 78 "x-webkit-" 79 }; 80 81 static inline bool shouldUpdateHeaderAfterRevalidation(const AtomicString& header) 82 { 83 for (size_t i = 0; i < WTF_ARRAY_LENGTH(headersToIgnoreAfterRevalidation); i++) { 84 if (equalIgnoringCase(header, headersToIgnoreAfterRevalidation[i])) 85 return false; 86 } 87 for (size_t i = 0; i < WTF_ARRAY_LENGTH(headerPrefixesToIgnoreAfterRevalidation); i++) { 88 if (header.startsWith(headerPrefixesToIgnoreAfterRevalidation[i], false)) 89 return false; 90 } 91 return true; 92 } 93 94 DEFINE_DEBUG_ONLY_GLOBAL(RefCountedLeakCounter, cachedResourceLeakCounter, ("Resource")); 95 96 Resource::Resource(const ResourceRequest& request, Type type) 97 : m_resourceRequest(request) 98 , m_responseTimestamp(currentTime()) 99 , m_cancelTimer(this, &Resource::cancelTimerFired) 100 , m_lastDecodedAccessTime(0) 101 , m_loadFinishTime(0) 102 , m_identifier(0) 103 , m_encodedSize(0) 104 , m_decodedSize(0) 105 , m_accessCount(0) 106 , m_handleCount(0) 107 , m_preloadCount(0) 108 , m_preloadResult(PreloadNotReferenced) 109 , m_cacheLiveResourcePriority(CacheLiveResourcePriorityLow) 110 , m_inLiveDecodedResourcesList(false) 111 , m_requestedFromNetworkingLayer(false) 112 , m_inCache(false) 113 , m_loading(false) 114 , m_switchingClientsToRevalidatedResource(false) 115 , m_type(type) 116 , m_status(Pending) 117 #ifndef NDEBUG 118 , m_deleted(false) 119 , m_lruIndex(0) 120 #endif 121 , m_nextInAllResourcesList(0) 122 , m_prevInAllResourcesList(0) 123 , m_nextInLiveResourcesList(0) 124 , m_prevInLiveResourcesList(0) 125 , m_resourceToRevalidate(0) 126 , m_proxyResource(0) 127 { 128 ASSERT(m_type == unsigned(type)); // m_type is a bitfield, so this tests careless updates of the enum. 129 #ifndef NDEBUG 130 cachedResourceLeakCounter.increment(); 131 #endif 132 133 if (!m_resourceRequest.url().hasFragmentIdentifier()) 134 return; 135 KURL urlForCache = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url()); 136 if (urlForCache.hasFragmentIdentifier()) 137 return; 138 m_fragmentIdentifierForRequest = m_resourceRequest.url().fragmentIdentifier(); 139 m_resourceRequest.setURL(urlForCache); 140 } 141 142 Resource::~Resource() 143 { 144 ASSERT(!m_resourceToRevalidate); // Should be true because canDelete() checks this. 145 ASSERT(canDelete()); 146 ASSERT(!inCache()); 147 ASSERT(!m_deleted); 148 ASSERT(url().isNull() || memoryCache()->resourceForURL(KURL(ParsedURLString, url())) != this); 149 150 #ifndef NDEBUG 151 m_deleted = true; 152 cachedResourceLeakCounter.decrement(); 153 #endif 154 } 155 156 void Resource::failBeforeStarting() 157 { 158 LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data()); 159 error(Resource::LoadError); 160 } 161 162 void Resource::load(ResourceFetcher* fetcher, const ResourceLoaderOptions& options) 163 { 164 if (!fetcher->frame()) { 165 failBeforeStarting(); 166 return; 167 } 168 169 m_options = options; 170 m_loading = true; 171 172 if (!accept().isEmpty()) 173 m_resourceRequest.setHTTPAccept(accept()); 174 175 // FIXME: It's unfortunate that the cache layer and below get to know anything about fragment identifiers. 176 // We should look into removing the expectation of that knowledge from the platform network stacks. 177 ResourceRequest request(m_resourceRequest); 178 if (!m_fragmentIdentifierForRequest.isNull()) { 179 KURL url = request.url(); 180 url.setFragmentIdentifier(m_fragmentIdentifierForRequest); 181 request.setURL(url); 182 m_fragmentIdentifierForRequest = String(); 183 } 184 185 m_loader = ResourceLoader::create(fetcher, this, request, options); 186 if (!m_loader) { 187 failBeforeStarting(); 188 return; 189 } 190 m_status = Pending; 191 } 192 193 void Resource::checkNotify() 194 { 195 if (isLoading()) 196 return; 197 198 ResourceClientWalker<ResourceClient> w(m_clients); 199 while (ResourceClient* c = w.next()) 200 c->notifyFinished(this); 201 } 202 203 void Resource::appendData(const char* data, int length) 204 { 205 ASSERT(!m_resourceToRevalidate); 206 ASSERT(!errorOccurred()); 207 if (m_options.dataBufferingPolicy == DoNotBufferData) 208 return; 209 if (m_data) 210 m_data->append(data, length); 211 else 212 m_data = SharedBuffer::create(data, length); 213 setEncodedSize(m_data->size()); 214 } 215 216 void Resource::error(Resource::Status status) 217 { 218 if (m_resourceToRevalidate) 219 revalidationFailed(); 220 221 if (!m_error.isNull() && (m_error.isCancellation() || !isPreloaded())) 222 memoryCache()->remove(this); 223 224 setStatus(status); 225 ASSERT(errorOccurred()); 226 m_data.clear(); 227 228 setLoading(false); 229 checkNotify(); 230 } 231 232 void Resource::finishOnePart() 233 { 234 setLoading(false); 235 checkNotify(); 236 } 237 238 void Resource::finish(double finishTime) 239 { 240 ASSERT(!m_resourceToRevalidate); 241 ASSERT(!errorOccurred()); 242 m_loadFinishTime = finishTime; 243 finishOnePart(); 244 if (!errorOccurred()) 245 m_status = Cached; 246 } 247 248 bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin) 249 { 250 String ignoredErrorDescription; 251 return passesAccessControlCheck(securityOrigin, ignoredErrorDescription); 252 } 253 254 bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin, String& errorDescription) 255 { 256 return WebCore::passesAccessControlCheck(m_response, resourceRequest().allowCookies() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription); 257 } 258 259 bool Resource::isExpired() const 260 { 261 if (m_response.isNull()) 262 return false; 263 264 return currentAge() > freshnessLifetime(); 265 } 266 267 double Resource::currentAge() const 268 { 269 // RFC2616 13.2.3 270 // No compensation for latency as that is not terribly important in practice 271 double dateValue = m_response.date(); 272 double apparentAge = std::isfinite(dateValue) ? std::max(0., m_responseTimestamp - dateValue) : 0; 273 double ageValue = m_response.age(); 274 double correctedReceivedAge = std::isfinite(ageValue) ? std::max(apparentAge, ageValue) : apparentAge; 275 double residentTime = currentTime() - m_responseTimestamp; 276 return correctedReceivedAge + residentTime; 277 } 278 279 double Resource::freshnessLifetime() const 280 { 281 // Cache non-http resources liberally 282 if (!m_response.url().protocolIsInHTTPFamily()) 283 return std::numeric_limits<double>::max(); 284 285 // RFC2616 13.2.4 286 double maxAgeValue = m_response.cacheControlMaxAge(); 287 if (std::isfinite(maxAgeValue)) 288 return maxAgeValue; 289 double expiresValue = m_response.expires(); 290 double dateValue = m_response.date(); 291 double creationTime = std::isfinite(dateValue) ? dateValue : m_responseTimestamp; 292 if (std::isfinite(expiresValue)) 293 return expiresValue - creationTime; 294 double lastModifiedValue = m_response.lastModified(); 295 if (std::isfinite(lastModifiedValue)) 296 return (creationTime - lastModifiedValue) * 0.1; 297 // If no cache headers are present, the specification leaves the decision to the UA. Other browsers seem to opt for 0. 298 return 0; 299 } 300 301 void Resource::responseReceived(const ResourceResponse& response) 302 { 303 setResponse(response); 304 m_responseTimestamp = currentTime(); 305 String encoding = response.textEncodingName(); 306 if (!encoding.isNull()) 307 setEncoding(encoding); 308 309 if (!m_resourceToRevalidate) 310 return; 311 if (response.httpStatusCode() == 304) 312 revalidationSucceeded(response); 313 else 314 revalidationFailed(); 315 } 316 317 void Resource::setSerializedCachedMetadata(const char* data, size_t size) 318 { 319 // We only expect to receive cached metadata from the platform once. 320 // If this triggers, it indicates an efficiency problem which is most 321 // likely unexpected in code designed to improve performance. 322 ASSERT(!m_cachedMetadata); 323 ASSERT(!m_resourceToRevalidate); 324 325 m_cachedMetadata = CachedMetadata::deserialize(data, size); 326 } 327 328 void Resource::setCachedMetadata(unsigned dataTypeID, const char* data, size_t size) 329 { 330 // Currently, only one type of cached metadata per resource is supported. 331 // If the need arises for multiple types of metadata per resource this could 332 // be enhanced to store types of metadata in a map. 333 ASSERT(!m_cachedMetadata); 334 335 m_cachedMetadata = CachedMetadata::create(dataTypeID, data, size); 336 const Vector<char>& serializedData = m_cachedMetadata->serialize(); 337 WebKit::Platform::current()->cacheMetadata(m_response.url(), m_response.responseTime(), serializedData.data(), serializedData.size()); 338 } 339 340 CachedMetadata* Resource::cachedMetadata(unsigned dataTypeID) const 341 { 342 if (!m_cachedMetadata || m_cachedMetadata->dataTypeID() != dataTypeID) 343 return 0; 344 return m_cachedMetadata.get(); 345 } 346 347 void Resource::setCacheLiveResourcePriority(CacheLiveResourcePriority priority) 348 { 349 if (inCache() && m_inLiveDecodedResourcesList && m_cacheLiveResourcePriority != priority) { 350 memoryCache()->removeFromLiveDecodedResourcesList(this); 351 m_cacheLiveResourcePriority = priority; 352 memoryCache()->insertInLiveDecodedResourcesList(this); 353 memoryCache()->prune(); 354 } 355 } 356 357 void Resource::clearLoader() 358 { 359 m_loader = 0; 360 } 361 362 void Resource::addClient(ResourceClient* client) 363 { 364 if (addClientToSet(client)) 365 didAddClient(client); 366 } 367 368 void Resource::didAddClient(ResourceClient* c) 369 { 370 if (m_clientsAwaitingCallback.contains(c)) { 371 m_clients.add(c); 372 m_clientsAwaitingCallback.remove(c); 373 } 374 if (!isLoading() && !stillNeedsLoad()) 375 c->notifyFinished(this); 376 } 377 378 bool Resource::addClientToSet(ResourceClient* 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 393 if ((m_type == Raw || m_type == MainResource) && !m_response.isNull() && !m_proxyResource) { 394 // Certain resources (especially XHRs and main resources) do crazy things if an asynchronous load returns 395 // synchronously (e.g., scripts may not have set all the state they need to handle the load). 396 // Therefore, rather than immediately sending callbacks on a cache hit like other Resources, 397 // we schedule the callbacks and ensure we never finish synchronously. 398 ASSERT(!m_clientsAwaitingCallback.contains(client)); 399 m_clientsAwaitingCallback.add(client, ResourceCallback::schedule(this, client)); 400 return false; 401 } 402 403 m_clients.add(client); 404 return true; 405 } 406 407 void Resource::removeClient(ResourceClient* client) 408 { 409 OwnPtr<ResourceCallback> callback = m_clientsAwaitingCallback.take(client); 410 if (callback) { 411 ASSERT(!m_clients.contains(client)); 412 callback->cancel(); 413 callback.clear(); 414 } else { 415 ASSERT(m_clients.contains(client)); 416 m_clients.remove(client); 417 didRemoveClient(client); 418 } 419 420 bool deleted = deleteIfPossible(); 421 if (!deleted && !hasClients()) { 422 if (inCache()) { 423 memoryCache()->removeFromLiveResourcesSize(this); 424 memoryCache()->removeFromLiveDecodedResourcesList(this); 425 } 426 if (!m_switchingClientsToRevalidatedResource) 427 allClientsRemoved(); 428 if (response().cacheControlContainsNoStore()) { 429 // RFC2616 14.9.2: 430 // "no-store: ... MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible" 431 // "... History buffers MAY store such responses as part of their normal operation." 432 // We allow non-secure content to be reused in history, but we do not allow secure content to be reused. 433 if (url().protocolIs("https")) 434 memoryCache()->remove(this); 435 } else { 436 memoryCache()->prune(); 437 } 438 } 439 // This object may be dead here. 440 } 441 442 void Resource::allClientsRemoved() 443 { 444 if (!m_loader) 445 return; 446 if (m_type == MainResource || m_type == Raw) 447 cancelTimerFired(&m_cancelTimer); 448 else if (!m_cancelTimer.isActive()) 449 m_cancelTimer.startOneShot(0); 450 } 451 452 void Resource::cancelTimerFired(Timer<Resource>* timer) 453 { 454 ASSERT_UNUSED(timer, timer == &m_cancelTimer); 455 if (hasClients() || !m_loader) 456 return; 457 ResourcePtr<Resource> protect(this); 458 m_loader->cancelIfNotFinishing(); 459 if (m_status != Cached) 460 memoryCache()->remove(this); 461 } 462 463 bool Resource::deleteIfPossible() 464 { 465 if (canDelete() && !inCache()) { 466 InspectorInstrumentation::willDestroyResource(this); 467 delete this; 468 return true; 469 } 470 return false; 471 } 472 473 void Resource::setDecodedSize(unsigned size) 474 { 475 if (size == m_decodedSize) 476 return; 477 478 int delta = size - m_decodedSize; 479 480 // The object must now be moved to a different queue, since its size has been changed. 481 // We have to remove explicitly before updating m_decodedSize, so that we find the correct previous 482 // queue. 483 if (inCache()) 484 memoryCache()->removeFromLRUList(this); 485 486 m_decodedSize = size; 487 488 if (inCache()) { 489 // Now insert into the new LRU list. 490 memoryCache()->insertInLRUList(this); 491 492 // Insert into or remove from the live decoded list if necessary. 493 // When inserting into the LiveDecodedResourcesList it is possible 494 // that the m_lastDecodedAccessTime is still zero or smaller than 495 // the m_lastDecodedAccessTime of the current list head. This is a 496 // violation of the invariant that the list is to be kept sorted 497 // by access time. The weakening of the invariant does not pose 498 // a problem. For more details please see: https://bugs.webkit.org/show_bug.cgi?id=30209 499 if (m_decodedSize && !m_inLiveDecodedResourcesList && hasClients()) 500 memoryCache()->insertInLiveDecodedResourcesList(this); 501 else if (!m_decodedSize && m_inLiveDecodedResourcesList) 502 memoryCache()->removeFromLiveDecodedResourcesList(this); 503 504 // Update the cache's size totals. 505 memoryCache()->adjustSize(hasClients(), delta); 506 } 507 } 508 509 void Resource::setEncodedSize(unsigned size) 510 { 511 if (size == m_encodedSize) 512 return; 513 514 int delta = size - m_encodedSize; 515 516 // The object must now be moved to a different queue, since its size has been changed. 517 // We have to remove explicitly before updating m_encodedSize, so that we find the correct previous 518 // queue. 519 if (inCache()) 520 memoryCache()->removeFromLRUList(this); 521 522 m_encodedSize = size; 523 524 if (inCache()) { 525 // Now insert into the new LRU list. 526 memoryCache()->insertInLRUList(this); 527 528 // Update the cache's size totals. 529 memoryCache()->adjustSize(hasClients(), delta); 530 } 531 } 532 533 void Resource::didAccessDecodedData(double timeStamp) 534 { 535 m_lastDecodedAccessTime = timeStamp; 536 if (inCache()) { 537 if (m_inLiveDecodedResourcesList) { 538 memoryCache()->removeFromLiveDecodedResourcesList(this); 539 memoryCache()->insertInLiveDecodedResourcesList(this); 540 } 541 memoryCache()->prune(); 542 } 543 } 544 545 void Resource::setResourceToRevalidate(Resource* resource) 546 { 547 ASSERT(resource); 548 ASSERT(!m_resourceToRevalidate); 549 ASSERT(resource != this); 550 ASSERT(m_handlesToRevalidate.isEmpty()); 551 ASSERT(resource->type() == type()); 552 553 LOG(ResourceLoading, "Resource %p setResourceToRevalidate %p", this, resource); 554 555 // The following assert should be investigated whenever it occurs. Although it should never fire, it currently does in rare circumstances. 556 // https://bugs.webkit.org/show_bug.cgi?id=28604. 557 // So the code needs to be robust to this assert failing thus the "if (m_resourceToRevalidate->m_proxyResource == this)" in Resource::clearResourceToRevalidate. 558 ASSERT(!resource->m_proxyResource); 559 560 resource->m_proxyResource = this; 561 m_resourceToRevalidate = resource; 562 } 563 564 void Resource::clearResourceToRevalidate() 565 { 566 ASSERT(m_resourceToRevalidate); 567 if (m_switchingClientsToRevalidatedResource) 568 return; 569 570 // 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. 571 if (m_resourceToRevalidate->m_proxyResource == this) { 572 m_resourceToRevalidate->m_proxyResource = 0; 573 m_resourceToRevalidate->deleteIfPossible(); 574 } 575 m_handlesToRevalidate.clear(); 576 m_resourceToRevalidate = 0; 577 deleteIfPossible(); 578 } 579 580 void Resource::switchClientsToRevalidatedResource() 581 { 582 ASSERT(m_resourceToRevalidate); 583 ASSERT(m_resourceToRevalidate->inCache()); 584 ASSERT(!inCache()); 585 586 LOG(ResourceLoading, "Resource %p switchClientsToRevalidatedResource %p", this, m_resourceToRevalidate); 587 588 m_resourceToRevalidate->m_identifier = m_identifier; 589 590 m_switchingClientsToRevalidatedResource = true; 591 HashSet<ResourcePtrBase*>::iterator end = m_handlesToRevalidate.end(); 592 for (HashSet<ResourcePtrBase*>::iterator it = m_handlesToRevalidate.begin(); it != end; ++it) { 593 ResourcePtrBase* handle = *it; 594 handle->m_resource = m_resourceToRevalidate; 595 m_resourceToRevalidate->registerHandle(handle); 596 --m_handleCount; 597 } 598 ASSERT(!m_handleCount); 599 m_handlesToRevalidate.clear(); 600 601 Vector<ResourceClient*> clientsToMove; 602 HashCountedSet<ResourceClient*>::iterator end2 = m_clients.end(); 603 for (HashCountedSet<ResourceClient*>::iterator it = m_clients.begin(); it != end2; ++it) { 604 ResourceClient* client = it->key; 605 unsigned count = it->value; 606 while (count) { 607 clientsToMove.append(client); 608 --count; 609 } 610 } 611 612 unsigned moveCount = clientsToMove.size(); 613 for (unsigned n = 0; n < moveCount; ++n) 614 removeClient(clientsToMove[n]); 615 ASSERT(m_clients.isEmpty()); 616 617 for (unsigned n = 0; n < moveCount; ++n) 618 m_resourceToRevalidate->addClientToSet(clientsToMove[n]); 619 for (unsigned n = 0; n < moveCount; ++n) { 620 // Calling didAddClient may do anything, including trying to cancel revalidation. 621 // Assert that it didn't succeed. 622 ASSERT(m_resourceToRevalidate); 623 // Calling didAddClient for a client may end up removing another client. In that case it won't be in the set anymore. 624 if (m_resourceToRevalidate->m_clients.contains(clientsToMove[n])) 625 m_resourceToRevalidate->didAddClient(clientsToMove[n]); 626 } 627 m_switchingClientsToRevalidatedResource = false; 628 } 629 630 void Resource::updateResponseAfterRevalidation(const ResourceResponse& validatingResponse) 631 { 632 m_responseTimestamp = currentTime(); 633 634 // RFC2616 10.3.5 635 // Update cached headers from the 304 response 636 const HTTPHeaderMap& newHeaders = validatingResponse.httpHeaderFields(); 637 HTTPHeaderMap::const_iterator end = newHeaders.end(); 638 for (HTTPHeaderMap::const_iterator it = newHeaders.begin(); it != end; ++it) { 639 // Entity headers should not be sent by servers when generating a 304 640 // response; misconfigured servers send them anyway. We shouldn't allow 641 // such headers to update the original request. We'll base this on the 642 // list defined by RFC2616 7.1, with a few additions for extension headers 643 // we care about. 644 if (!shouldUpdateHeaderAfterRevalidation(it->key)) 645 continue; 646 m_response.setHTTPHeaderField(it->key, it->value); 647 } 648 } 649 650 void Resource::revalidationSucceeded(const ResourceResponse& response) 651 { 652 ASSERT(m_resourceToRevalidate); 653 ASSERT(!m_resourceToRevalidate->inCache()); 654 ASSERT(m_resourceToRevalidate->isLoaded()); 655 ASSERT(inCache()); 656 657 // Calling evict() can potentially delete revalidatingResource, which we use 658 // below. This mustn't be the case since revalidation means it is loaded 659 // and so canDelete() is false. 660 ASSERT(!canDelete()); 661 662 m_resourceToRevalidate->updateResponseAfterRevalidation(response); 663 memoryCache()->replace(m_resourceToRevalidate, this); 664 665 switchClientsToRevalidatedResource(); 666 ASSERT(!m_deleted); 667 // clearResourceToRevalidate deletes this. 668 clearResourceToRevalidate(); 669 } 670 671 void Resource::revalidationFailed() 672 { 673 ASSERT(WTF::isMainThread()); 674 LOG(ResourceLoading, "Revalidation failed for %p", this); 675 ASSERT(resourceToRevalidate()); 676 clearResourceToRevalidate(); 677 } 678 679 void Resource::updateForAccess() 680 { 681 ASSERT(inCache()); 682 683 // Need to make sure to remove before we increase the access count, since 684 // the queue will possibly change. 685 memoryCache()->removeFromLRUList(this); 686 687 // If this is the first time the resource has been accessed, adjust the size of the cache to account for its initial size. 688 if (!m_accessCount) 689 memoryCache()->adjustSize(hasClients(), size()); 690 691 m_accessCount++; 692 memoryCache()->insertInLRUList(this); 693 } 694 695 void Resource::registerHandle(ResourcePtrBase* h) 696 { 697 ++m_handleCount; 698 if (m_resourceToRevalidate) 699 m_handlesToRevalidate.add(h); 700 } 701 702 void Resource::unregisterHandle(ResourcePtrBase* h) 703 { 704 ASSERT(m_handleCount > 0); 705 --m_handleCount; 706 707 if (m_resourceToRevalidate) 708 m_handlesToRevalidate.remove(h); 709 710 if (!m_handleCount) 711 deleteIfPossible(); 712 } 713 714 bool Resource::canUseCacheValidator() const 715 { 716 if (m_loading || errorOccurred()) 717 return false; 718 719 if (m_response.cacheControlContainsNoStore()) 720 return false; 721 return m_response.hasCacheValidatorFields(); 722 } 723 724 bool Resource::mustRevalidateDueToCacheHeaders(CachePolicy cachePolicy) const 725 { 726 ASSERT(cachePolicy == CachePolicyRevalidate || cachePolicy == CachePolicyCache || cachePolicy == CachePolicyVerify); 727 728 if (cachePolicy == CachePolicyRevalidate) 729 return true; 730 731 if (m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()) { 732 LOG(ResourceLoading, "Resource %p mustRevalidate because of m_response.cacheControlContainsNoCache() || m_response.cacheControlContainsNoStore()\n", this); 733 return true; 734 } 735 736 if (cachePolicy == CachePolicyCache) { 737 if (m_response.cacheControlContainsMustRevalidate() && isExpired()) { 738 LOG(ResourceLoading, "Resource %p mustRevalidate because of cachePolicy == CachePolicyCache and m_response.cacheControlContainsMustRevalidate() && isExpired()\n", this); 739 return true; 740 } 741 return false; 742 } 743 744 // CachePolicyVerify 745 if (isExpired()) { 746 LOG(ResourceLoading, "Resource %p mustRevalidate because of isExpired()\n", this); 747 return true; 748 } 749 750 return false; 751 } 752 753 bool Resource::isSafeToMakePurgeable() const 754 { 755 return !hasClients() && !m_proxyResource && !m_resourceToRevalidate; 756 } 757 758 bool Resource::makePurgeable(bool purgeable) 759 { 760 if (purgeable) { 761 ASSERT(isSafeToMakePurgeable()); 762 763 if (m_purgeableData) { 764 ASSERT(!m_data); 765 return true; 766 } 767 if (!m_data) 768 return false; 769 770 // Should not make buffer purgeable if it has refs other than this since we don't want two copies. 771 if (!m_data->hasOneRef()) 772 return false; 773 774 m_data->createPurgeableBuffer(); 775 if (!m_data->hasPurgeableBuffer()) 776 return false; 777 778 m_purgeableData = m_data->releasePurgeableBuffer(); 779 m_purgeableData->unlock(); 780 m_data.clear(); 781 return true; 782 } 783 784 if (!m_purgeableData) 785 return true; 786 787 ASSERT(!m_data); 788 ASSERT(!hasClients()); 789 790 if (!m_purgeableData->lock()) 791 return false; 792 793 m_data = SharedBuffer::adoptPurgeableBuffer(m_purgeableData.release()); 794 return true; 795 } 796 797 bool Resource::isPurgeable() const 798 { 799 return m_purgeableData && m_purgeableData->isPurgeable(); 800 } 801 802 bool Resource::wasPurged() const 803 { 804 return m_purgeableData && m_purgeableData->wasPurged(); 805 } 806 807 unsigned Resource::overheadSize() const 808 { 809 static const int kAverageClientsHashMapSize = 384; 810 return sizeof(Resource) + m_response.memoryUsage() + kAverageClientsHashMapSize + m_resourceRequest.url().string().length() * 2; 811 } 812 813 void Resource::didChangePriority(ResourceLoadPriority loadPriority) 814 { 815 if (m_loader) 816 m_loader->didChangePriority(loadPriority); 817 } 818 819 Resource::ResourceCallback::ResourceCallback(Resource* resource, ResourceClient* client) 820 : m_resource(resource) 821 , m_client(client) 822 , m_callbackTimer(this, &ResourceCallback::timerFired) 823 { 824 m_callbackTimer.startOneShot(0); 825 } 826 827 void Resource::ResourceCallback::cancel() 828 { 829 if (m_callbackTimer.isActive()) 830 m_callbackTimer.stop(); 831 } 832 833 void Resource::ResourceCallback::timerFired(Timer<ResourceCallback>*) 834 { 835 m_resource->didAddClient(m_client); 836 } 837 838 } 839 840