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