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