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 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 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 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 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 157 void Resource::dispose() 158 { 159 } 160 161 void Resource::trace(Visitor* visitor) 162 { 163 visitor->trace(m_loader); 164 visitor->trace(m_resourceToRevalidate); 165 visitor->trace(m_proxyResource); 166 } 167 168 void Resource::failBeforeStarting() 169 { 170 WTF_LOG(ResourceLoading, "Cannot start loading '%s'", url().string().latin1().data()); 171 error(Resource::LoadError); 172 } 173 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 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 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 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 239 void Resource::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy) 240 { 241 m_options.dataBufferingPolicy = dataBufferingPolicy; 242 m_data.clear(); 243 setEncodedSize(0); 244 } 245 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 262 void Resource::finishOnePart() 263 { 264 setLoading(false); 265 checkNotify(); 266 } 267 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 278 bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin) 279 { 280 String ignoredErrorDescription; 281 return passesAccessControlCheck(securityOrigin, ignoredErrorDescription); 282 } 283 284 bool Resource::passesAccessControlCheck(SecurityOrigin* securityOrigin, String& errorDescription) 285 { 286 return blink::passesAccessControlCheck(m_response, resourceRequest().allowStoredCredentials() ? AllowStoredCredentials : DoNotAllowStoredCredentials, securityOrigin, errorDescription); 287 } 288 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 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 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 356 const ResourceRequest& Resource::lastResourceRequest() const 357 { 358 if (!m_redirectChain.size()) 359 return m_resourceRequest; 360 return m_redirectChain.last().m_request; 361 } 362 363 void Resource::willSendRequest(ResourceRequest& request, const ResourceResponse& response) 364 { 365 m_redirectChain.append(RedirectPair(request, response)); 366 m_requestedFromNetworkingLayer = true; 367 } 368 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 384 bool Resource::hasRightHandleCountApartFromCache(unsigned targetCount) const 385 { 386 return m_handleCount == targetCount + (memoryCache()->contains(this) ? 1 : 0); 387 } 388 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 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 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 431 void Resource::clearCachedMetadata() 432 { 433 m_cachedMetadata.clear(); 434 } 435 436 bool Resource::canDelete() const 437 { 438 return !hasClients() && !m_loader && !m_preloadCount && hasRightHandleCountApartFromCache(0) 439 && !m_protectorCount && !m_resourceToRevalidate && !m_proxyResource; 440 } 441 442 bool Resource::hasOneHandle() const 443 { 444 return hasRightHandleCountApartFromCache(1); 445 } 446 447 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 454 void Resource::clearLoader() 455 { 456 m_loader = nullptr; 457 } 458 459 void Resource::addClient(ResourceClient* client) 460 { 461 if (addClientToSet(client)) 462 didAddClient(client); 463 } 464 465 void Resource::didAddClient(ResourceClient* c) 466 { 467 if (!isLoading() && !stillNeedsLoad()) 468 c->notifyFinished(this); 469 } 470 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 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 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 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 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 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 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 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 606 void Resource::didAccessDecodedData() 607 { 608 memoryCache()->updateDecodedResource(this, UpdateForAccess); 609 memoryCache()->prune(); 610 } 611 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 646 void Resource::prune() 647 { 648 destroyDecodedDataIfPossible(); 649 unlock(); 650 } 651 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 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 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 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 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 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 785 void Resource::registerHandle(ResourcePtrBase* h) 786 { 787 assertAlive(); 788 ++m_handleCount; 789 if (m_resourceToRevalidate) 790 m_handlesToRevalidate.add(h); 791 } 792 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 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 824 bool Resource::hasCacheControlNoStoreHeader() 825 { 826 return m_response.cacheControlContainsNoStore() || m_resourceRequest.cacheControlContainsNoStore(); 827 } 828 829 bool Resource::mustRevalidateDueToCacheHeaders() 830 { 831 return !canUseResponse(m_response, m_responseTimestamp) || m_resourceRequest.cacheControlContainsNoCache() || m_resourceRequest.cacheControlContainsNoStore(); 832 } 833 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 844 bool Resource::isPurgeable() const 845 { 846 return m_data && !m_data->isLocked(); 847 } 848 849 bool Resource::wasPurged() const 850 { 851 return m_wasPurged; 852 } 853 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 870 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 876 void Resource::didChangePriority(ResourceLoadPriority loadPriority, int intraPriorityValue) 877 { 878 if (m_loader) 879 m_loader->didChangePriority(loadPriority, intraPriorityValue); 880 } 881 882 Resource::ResourceCallback* Resource::ResourceCallback::callbackHandler() 883 { 884 DEFINE_STATIC_LOCAL(ResourceCallback, callbackHandler, ()); 885 return &callbackHandler; 886 } 887 888 Resource::ResourceCallback::ResourceCallback() 889 : m_callbackTimer(this, &ResourceCallback::timerFired) 890 { 891 } 892 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 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 909 bool Resource::ResourceCallback::isScheduled(Resource* resource) const 910 { 911 return m_resourcesWithPendingClients.contains(resource); 912 } 913 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 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 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 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