| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) | |
| 3 Copyright (C) 2001 Dirk Mueller (mueller@kde.org) | |
| 4 Copyright (C) 2002 Waldo Bastian (bastian@kde.org) | |
| 5 Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
rights reserved. | |
| 6 Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ | |
| 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 This class provides all functionality needed for loading images, style sheet
s and html | |
| 24 pages from the web. It has a memory cache for these objects. | |
| 25 */ | |
| 26 | |
| 27 #include "sky/engine/core/fetch/ResourceFetcher.h" | |
| 28 | |
| 29 #include "gen/sky/core/FetchInitiatorTypeNames.h" | |
| 30 #include "gen/sky/platform/RuntimeEnabledFeatures.h" | |
| 31 #include "sky/engine/core/dom/Document.h" | |
| 32 #include "sky/engine/core/fetch/FetchContext.h" | |
| 33 #include "sky/engine/core/fetch/FontResource.h" | |
| 34 #include "sky/engine/core/fetch/ImageResource.h" | |
| 35 #include "sky/engine/core/fetch/MemoryCache.h" | |
| 36 #include "sky/engine/core/fetch/RawResource.h" | |
| 37 #include "sky/engine/core/fetch/ResourceLoader.h" | |
| 38 #include "sky/engine/core/fetch/ResourceLoaderSet.h" | |
| 39 #include "sky/engine/core/frame/LocalDOMWindow.h" | |
| 40 #include "sky/engine/core/frame/LocalFrame.h" | |
| 41 #include "sky/engine/core/frame/Settings.h" | |
| 42 #include "sky/engine/core/html/HTMLElement.h" | |
| 43 #include "sky/engine/core/inspector/ConsoleMessage.h" | |
| 44 #include "sky/engine/core/loader/FrameLoaderClient.h" | |
| 45 #include "sky/engine/core/loader/UniqueIdentifier.h" | |
| 46 #include "sky/engine/core/page/Page.h" | |
| 47 #include "sky/engine/platform/Logging.h" | |
| 48 #include "sky/engine/platform/SharedBuffer.h" | |
| 49 #include "sky/engine/platform/TraceEvent.h" | |
| 50 #include "sky/engine/platform/weborigin/SecurityPolicy.h" | |
| 51 #include "sky/engine/public/platform/Platform.h" | |
| 52 #include "sky/engine/public/platform/WebURL.h" | |
| 53 #include "sky/engine/public/platform/WebURLRequest.h" | |
| 54 #include "sky/engine/wtf/text/CString.h" | |
| 55 #include "sky/engine/wtf/text/WTFString.h" | |
| 56 | |
| 57 using blink::WebURLRequest; | |
| 58 | |
| 59 namespace blink { | |
| 60 | |
| 61 static Resource* createResource(Resource::Type type, const ResourceRequest& requ
est, const String& charset) | |
| 62 { | |
| 63 switch (type) { | |
| 64 case Resource::Image: | |
| 65 return new ImageResource(request); | |
| 66 case Resource::Font: | |
| 67 return new FontResource(request); | |
| 68 case Resource::MainResource: | |
| 69 case Resource::Raw: | |
| 70 case Resource::Media: | |
| 71 return new RawResource(request, type); | |
| 72 case Resource::LinkPrefetch: | |
| 73 return new Resource(request, Resource::LinkPrefetch); | |
| 74 case Resource::LinkSubresource: | |
| 75 return new Resource(request, Resource::LinkSubresource); | |
| 76 case Resource::ImportResource: | |
| 77 return new RawResource(request, type); | |
| 78 } | |
| 79 | |
| 80 ASSERT_NOT_REACHED(); | |
| 81 return 0; | |
| 82 } | |
| 83 | |
| 84 static ResourceLoadPriority loadPriority(Resource::Type type, const FetchRequest
& request) | |
| 85 { | |
| 86 if (request.priority() != ResourceLoadPriorityUnresolved) | |
| 87 return request.priority(); | |
| 88 | |
| 89 switch (type) { | |
| 90 case Resource::MainResource: | |
| 91 return ResourceLoadPriorityVeryHigh; | |
| 92 case Resource::Raw: | |
| 93 case Resource::Font: | |
| 94 case Resource::ImportResource: | |
| 95 return ResourceLoadPriorityMedium; | |
| 96 case Resource::Image: | |
| 97 // We'll default images to VeryLow, and promote whatever is visible. Thi
s improves | |
| 98 // speed-index by ~5% on average, ~14% at the 99th percentile. | |
| 99 return ResourceLoadPriorityVeryLow; | |
| 100 case Resource::Media: | |
| 101 return ResourceLoadPriorityLow; | |
| 102 case Resource::LinkPrefetch: | |
| 103 return ResourceLoadPriorityVeryLow; | |
| 104 case Resource::LinkSubresource: | |
| 105 return ResourceLoadPriorityLow; | |
| 106 } | |
| 107 ASSERT_NOT_REACHED(); | |
| 108 return ResourceLoadPriorityUnresolved; | |
| 109 } | |
| 110 | |
| 111 static WebURLRequest::RequestContext requestContextFromType(const ResourceFetche
r* fetcher, Resource::Type type) | |
| 112 { | |
| 113 switch (type) { | |
| 114 case Resource::MainResource: | |
| 115 // FIXME: Change this to a context frame type (once we introduce them):
http://fetch.spec.whatwg.org/#concept-request-context-frame-type | |
| 116 return WebURLRequest::RequestContextHyperlink; | |
| 117 case Resource::Font: | |
| 118 return WebURLRequest::RequestContextFont; | |
| 119 case Resource::Image: | |
| 120 return WebURLRequest::RequestContextImage; | |
| 121 case Resource::Raw: | |
| 122 return WebURLRequest::RequestContextSubresource; | |
| 123 case Resource::ImportResource: | |
| 124 return WebURLRequest::RequestContextImport; | |
| 125 case Resource::LinkPrefetch: | |
| 126 return WebURLRequest::RequestContextPrefetch; | |
| 127 case Resource::LinkSubresource: | |
| 128 return WebURLRequest::RequestContextSubresource; | |
| 129 case Resource::Media: // TODO: Split this. | |
| 130 return WebURLRequest::RequestContextVideo; | |
| 131 } | |
| 132 ASSERT_NOT_REACHED(); | |
| 133 return WebURLRequest::RequestContextSubresource; | |
| 134 } | |
| 135 | |
| 136 ResourceFetcher::ResourceFetcher(Document* document) | |
| 137 : m_document(document) | |
| 138 , m_requestCount(0) | |
| 139 , m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageColl
ectDocumentResourcesTimerFired) | |
| 140 , m_autoLoadImages(true) | |
| 141 , m_imagesEnabled(true) | |
| 142 , m_allowStaleResources(false) | |
| 143 { | |
| 144 } | |
| 145 | |
| 146 ResourceFetcher::~ResourceFetcher() | |
| 147 { | |
| 148 m_document = nullptr; | |
| 149 | |
| 150 // Make sure no requests still point to this ResourceFetcher | |
| 151 ASSERT(!m_requestCount); | |
| 152 } | |
| 153 | |
| 154 Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const | |
| 155 { | |
| 156 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL); | |
| 157 return m_documentResources.get(url).get(); | |
| 158 } | |
| 159 | |
| 160 LocalFrame* ResourceFetcher::frame() const | |
| 161 { | |
| 162 // FIXME(sky): This used to prefer DocumentLoader::frame | |
| 163 // over importsController->master()->frame(), but in our | |
| 164 // world we should always just have one frame. | |
| 165 if (!m_document) | |
| 166 return 0; | |
| 167 return m_document->frame(); | |
| 168 } | |
| 169 | |
| 170 FetchContext& ResourceFetcher::context() const | |
| 171 { | |
| 172 return FetchContext::nullInstance(); | |
| 173 } | |
| 174 | |
| 175 ResourcePtr<ImageResource> ResourceFetcher::fetchImage(FetchRequest& request) | |
| 176 { | |
| 177 request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchR
equest::DeferredByClient : FetchRequest::NoDefer); | |
| 178 ResourcePtr<Resource> resource = requestResource(Resource::Image, request); | |
| 179 return resource && resource->type() == Resource::Image ? toImageResource(res
ource) : 0; | |
| 180 } | |
| 181 | |
| 182 ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request) | |
| 183 { | |
| 184 ASSERT(request.resourceRequest().frameType() == WebURLRequest::FrameTypeNone
); | |
| 185 request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestCon
textFont); | |
| 186 return toFontResource(requestResource(Resource::Font, request)); | |
| 187 } | |
| 188 | |
| 189 bool ResourceFetcher::canRequest(Resource::Type type, const KURL& url, const Res
ourceLoaderOptions& options, FetchRequest::OriginRestriction originRestriction)
const | |
| 190 { | |
| 191 // FIXME(sky): Remove | |
| 192 return true; | |
| 193 } | |
| 194 | |
| 195 bool ResourceFetcher::shouldLoadNewResource(Resource::Type type) const | |
| 196 { | |
| 197 if (!frame()) | |
| 198 return false; | |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 bool ResourceFetcher::resourceNeedsLoad(Resource* resource, const FetchRequest&
request, RevalidationPolicy policy) | |
| 203 { | |
| 204 if (FetchRequest::DeferredByClient == request.defer()) | |
| 205 return false; | |
| 206 if (policy != Use) | |
| 207 return true; | |
| 208 return resource->stillNeedsLoad(); | |
| 209 } | |
| 210 | |
| 211 void ResourceFetcher::requestLoadStarted(Resource* resource, const FetchRequest&
request, ResourceLoadStartType type) | |
| 212 { | |
| 213 if (type == ResourceLoadingFromCache) | |
| 214 notifyLoadedFromMemoryCache(resource); | |
| 215 | |
| 216 if (request.resourceRequest().url().protocolIsData()) | |
| 217 return; | |
| 218 | |
| 219 m_validatedURLs.add(request.resourceRequest().url()); | |
| 220 } | |
| 221 | |
| 222 ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, Fetc
hRequest& request) | |
| 223 { | |
| 224 ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type
== Resource::Raw); | |
| 225 | |
| 226 TRACE_EVENT0("blink", "ResourceFetcher::requestResource"); | |
| 227 | |
| 228 KURL url = request.resourceRequest().url(); | |
| 229 | |
| 230 WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource '%s', charset '%s
', priority=%d, type=%s", url.elidedString().latin1().data(), request.charset().
latin1().data(), request.priority(), ResourceTypeName(type)); | |
| 231 | |
| 232 // If only the fragment identifiers differ, it is the same resource. | |
| 233 url = MemoryCache::removeFragmentIdentifierIfNeeded(url); | |
| 234 | |
| 235 if (!url.isValid()) | |
| 236 return 0; | |
| 237 | |
| 238 if (!canRequest(type, url, request.options(), request.originRestriction())) | |
| 239 return 0; | |
| 240 | |
| 241 if (LocalFrame* f = frame()) | |
| 242 f->loaderClient()->dispatchWillRequestResource(&request); | |
| 243 | |
| 244 // See if we can use an existing resource from the cache. | |
| 245 ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url); | |
| 246 | |
| 247 const RevalidationPolicy policy = determineRevalidationPolicy(type, request,
resource.get()); | |
| 248 switch (policy) { | |
| 249 case Reload: | |
| 250 memoryCache()->remove(resource.get()); | |
| 251 // Fall through | |
| 252 case Load: | |
| 253 resource = createResourceForLoading(type, request, request.charset()); | |
| 254 break; | |
| 255 case Revalidate: | |
| 256 resource = createResourceForRevalidation(request, resource.get()); | |
| 257 break; | |
| 258 case Use: | |
| 259 memoryCache()->updateForAccess(resource.get()); | |
| 260 break; | |
| 261 } | |
| 262 | |
| 263 if (!resource) | |
| 264 return 0; | |
| 265 | |
| 266 if (!resource->hasClients()) | |
| 267 m_deadStatsRecorder.update(policy); | |
| 268 | |
| 269 if (policy != Use) | |
| 270 resource->setIdentifier(createUniqueIdentifier()); | |
| 271 | |
| 272 ResourceLoadPriority priority = loadPriority(type, request); | |
| 273 if (priority != resource->resourceRequest().priority()) { | |
| 274 resource->mutableResourceRequest().setPriority(priority); | |
| 275 resource->didChangePriority(priority, 0); | |
| 276 } | |
| 277 | |
| 278 if (resourceNeedsLoad(resource.get(), request, policy)) { | |
| 279 if (!shouldLoadNewResource(type)) { | |
| 280 if (memoryCache()->contains(resource.get())) | |
| 281 memoryCache()->remove(resource.get()); | |
| 282 return 0; | |
| 283 } | |
| 284 | |
| 285 resource->load(this, request.options()); | |
| 286 | |
| 287 // For asynchronous loads that immediately fail, it's sufficient to retu
rn a | |
| 288 // null Resource, as it indicates that something prevented the load from
starting. | |
| 289 // If there's a network error, that failure will happen asynchronously.
However, if | |
| 290 // a sync load receives a network error, it will have already happened b
y this point. | |
| 291 // In that case, the requester should have access to the relevant Resour
ceError, so | |
| 292 // we need to return a non-null Resource. | |
| 293 if (resource->errorOccurred()) { | |
| 294 if (memoryCache()->contains(resource.get())) | |
| 295 memoryCache()->remove(resource.get()); | |
| 296 return 0; | |
| 297 } | |
| 298 } | |
| 299 | |
| 300 requestLoadStarted(resource.get(), request, policy == Use ? ResourceLoadingF
romCache : ResourceLoadingFromNetwork); | |
| 301 | |
| 302 ASSERT(resource->url() == url.string()); | |
| 303 m_documentResources.set(resource->url(), resource); | |
| 304 return resource; | |
| 305 } | |
| 306 | |
| 307 void ResourceFetcher::determineRequestContext(ResourceRequest& request, Resource
::Type type) | |
| 308 { | |
| 309 WebURLRequest::RequestContext requestContext = requestContextFromType(this,
type); | |
| 310 request.setRequestContext(requestContext); | |
| 311 } | |
| 312 | |
| 313 ResourceRequestCachePolicy ResourceFetcher::resourceRequestCachePolicy(const Res
ourceRequest& request, Resource::Type type) | |
| 314 { | |
| 315 if (request.isConditional()) | |
| 316 return ReloadIgnoringCacheData; | |
| 317 | |
| 318 return UseProtocolCachePolicy; | |
| 319 } | |
| 320 | |
| 321 void ResourceFetcher::addAdditionalRequestHeaders(ResourceRequest& request, Reso
urce::Type type) | |
| 322 { | |
| 323 if (!frame()) | |
| 324 return; | |
| 325 | |
| 326 if (request.cachePolicy() == UseProtocolCachePolicy) | |
| 327 request.setCachePolicy(resourceRequestCachePolicy(request, type)); | |
| 328 if (request.requestContext() == WebURLRequest::RequestContextUnspecified) | |
| 329 determineRequestContext(request, type); | |
| 330 if (type == Resource::LinkPrefetch || type == Resource::LinkSubresource) | |
| 331 request.setHTTPHeaderField("Purpose", "prefetch"); | |
| 332 | |
| 333 context().addAdditionalRequestHeaders(document(), request, (type == Resource
::MainResource) ? FetchMainResource : FetchSubresource); | |
| 334 } | |
| 335 | |
| 336 ResourcePtr<Resource> ResourceFetcher::createResourceForRevalidation(const Fetch
Request& request, Resource* resource) | |
| 337 { | |
| 338 ASSERT(resource); | |
| 339 ASSERT(memoryCache()->contains(resource)); | |
| 340 ASSERT(resource->isLoaded()); | |
| 341 ASSERT(resource->canUseCacheValidator()); | |
| 342 ASSERT(!resource->resourceToRevalidate()); | |
| 343 | |
| 344 ResourceRequest revalidatingRequest(resource->resourceRequest()); | |
| 345 revalidatingRequest.clearHTTPReferrer(); | |
| 346 addAdditionalRequestHeaders(revalidatingRequest, resource->type()); | |
| 347 | |
| 348 const AtomicString& lastModified = resource->response().httpHeaderField("Las
t-Modified"); | |
| 349 const AtomicString& eTag = resource->response().httpHeaderField("ETag"); | |
| 350 if (!lastModified.isEmpty() || !eTag.isEmpty()) { | |
| 351 ASSERT(context().cachePolicy(document()) != CachePolicyReload); | |
| 352 if (context().cachePolicy(document()) == CachePolicyRevalidate) | |
| 353 revalidatingRequest.setHTTPHeaderField("Cache-Control", "max-age=0")
; | |
| 354 } | |
| 355 if (!lastModified.isEmpty()) | |
| 356 revalidatingRequest.setHTTPHeaderField("If-Modified-Since", lastModified
); | |
| 357 if (!eTag.isEmpty()) | |
| 358 revalidatingRequest.setHTTPHeaderField("If-None-Match", eTag); | |
| 359 | |
| 360 ResourcePtr<Resource> newResource = createResource(resource->type(), revalid
atingRequest, resource->encoding()); | |
| 361 WTF_LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource
.get(), resource); | |
| 362 | |
| 363 newResource->setResourceToRevalidate(resource); | |
| 364 | |
| 365 memoryCache()->remove(resource); | |
| 366 memoryCache()->add(newResource.get()); | |
| 367 return newResource; | |
| 368 } | |
| 369 | |
| 370 ResourcePtr<Resource> ResourceFetcher::createResourceForLoading(Resource::Type t
ype, FetchRequest& request, const String& charset) | |
| 371 { | |
| 372 ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url())); | |
| 373 | |
| 374 WTF_LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceReque
st().url().elidedString().latin1().data()); | |
| 375 | |
| 376 addAdditionalRequestHeaders(request.mutableResourceRequest(), type); | |
| 377 ResourcePtr<Resource> resource = createResource(type, request.resourceReques
t(), charset); | |
| 378 | |
| 379 memoryCache()->add(resource.get()); | |
| 380 return resource; | |
| 381 } | |
| 382 | |
| 383 ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy
(Resource::Type type, const FetchRequest& fetchRequest, Resource* existingResour
ce) const | |
| 384 { | |
| 385 const ResourceRequest& request = fetchRequest.resourceRequest(); | |
| 386 | |
| 387 if (!existingResource) | |
| 388 return Load; | |
| 389 | |
| 390 // If the same URL has been loaded as a different type, we need to reload. | |
| 391 if (existingResource->type() != type) { | |
| 392 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy r
eloading due to type mismatch."); | |
| 393 return Reload; | |
| 394 } | |
| 395 | |
| 396 // Do not load from cache if images are not enabled. The load for this image
will be blocked | |
| 397 // in ImageResource::load. | |
| 398 if (FetchRequest::DeferredByClient == fetchRequest.defer()) | |
| 399 return Reload; | |
| 400 | |
| 401 // Always use data uris. | |
| 402 // FIXME: Extend this to non-images. | |
| 403 if (type == Resource::Image && request.url().protocolIsData()) | |
| 404 return Use; | |
| 405 | |
| 406 if (!existingResource->canReuse(request)) | |
| 407 return Reload; | |
| 408 | |
| 409 // Never use cache entries for downloadToFile requests. The caller expects t
he resource in a file. | |
| 410 if (request.downloadToFile()) | |
| 411 return Reload; | |
| 412 | |
| 413 // Certain requests (e.g., XHRs) might have manually set headers that requir
e revalidation. | |
| 414 // FIXME: In theory, this should be a Revalidate case. In practice, the Memo
ryCache revalidation path assumes a whole bunch | |
| 415 // of things about how revalidation works that manual headers violate, so pu
nt to Reload instead. | |
| 416 if (request.isConditional()) | |
| 417 return Reload; | |
| 418 | |
| 419 // Don't reload resources while pasting. | |
| 420 if (m_allowStaleResources) | |
| 421 return Use; | |
| 422 | |
| 423 if (!fetchRequest.options().canReuseRequest(existingResource->options())) | |
| 424 return Reload; | |
| 425 | |
| 426 // CachePolicyHistoryBuffer uses the cache no matter what. | |
| 427 CachePolicy cachePolicy = context().cachePolicy(document()); | |
| 428 if (cachePolicy == CachePolicyHistoryBuffer) | |
| 429 return Use; | |
| 430 | |
| 431 // Don't reuse resources with Cache-control: no-store. | |
| 432 if (existingResource->hasCacheControlNoStoreHeader()) { | |
| 433 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy r
eloading due to Cache-control: no-store."); | |
| 434 return Reload; | |
| 435 } | |
| 436 | |
| 437 // If credentials were sent with the previous request and won't be | |
| 438 // with this one, or vice versa, re-fetch the resource. | |
| 439 // | |
| 440 // This helps with the case where the server sends back | |
| 441 // "Access-Control-Allow-Origin: *" all the time, but some of the | |
| 442 // client's requests are made without CORS and some with. | |
| 443 if (existingResource->resourceRequest().allowStoredCredentials() != request.
allowStoredCredentials()) { | |
| 444 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy r
eloading due to difference in credentials settings."); | |
| 445 return Reload; | |
| 446 } | |
| 447 | |
| 448 // During the initial load, avoid loading the same resource multiple times f
or a single document, | |
| 449 // even if the cache policies would tell us to. | |
| 450 // We also group loads of the same resource together. | |
| 451 // Raw resources are exempted, as XHRs fall into this category and may have
user-set Cache-Control: | |
| 452 // headers or other factors that require separate requests. | |
| 453 if (type != Resource::Raw) { | |
| 454 if (document() && !document()->loadEventFinished() && m_validatedURLs.co
ntains(existingResource->url())) | |
| 455 return Use; | |
| 456 if (existingResource->isLoading()) | |
| 457 return Use; | |
| 458 } | |
| 459 | |
| 460 // CachePolicyReload always reloads | |
| 461 if (cachePolicy == CachePolicyReload) { | |
| 462 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy r
eloading due to CachePolicyReload."); | |
| 463 return Reload; | |
| 464 } | |
| 465 | |
| 466 // We'll try to reload the resource if it failed last time. | |
| 467 if (existingResource->errorOccurred()) { | |
| 468 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye
reloading due to resource being in the error state"); | |
| 469 return Reload; | |
| 470 } | |
| 471 | |
| 472 // List of available images logic allows images to be re-used without cache
validation. We restrict this only to images | |
| 473 // from memory cache which are the same as the version in the current docume
nt. | |
| 474 if (type == Resource::Image && existingResource == cachedResource(request.ur
l())) | |
| 475 return Use; | |
| 476 | |
| 477 // Check if the cache headers requires us to revalidate (cache expiration fo
r example). | |
| 478 if (cachePolicy == CachePolicyRevalidate || existingResource->mustRevalidate
DueToCacheHeaders() | |
| 479 || request.cacheControlContainsNoCache()) { | |
| 480 // See if the resource has usable ETag or Last-modified headers. | |
| 481 if (existingResource->canUseCacheValidator()) | |
| 482 return Revalidate; | |
| 483 | |
| 484 // No, must reload. | |
| 485 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy r
eloading due to missing cache validators."); | |
| 486 return Reload; | |
| 487 } | |
| 488 | |
| 489 return Use; | |
| 490 } | |
| 491 | |
| 492 void ResourceFetcher::printAccessDeniedMessage(const KURL& url) const | |
| 493 { | |
| 494 if (url.isNull()) | |
| 495 return; | |
| 496 | |
| 497 if (!frame()) | |
| 498 return; | |
| 499 | |
| 500 String message; | |
| 501 if (!m_document || m_document->url().isNull()) | |
| 502 message = "Unsafe attempt to load URL " + url.elidedString() + '.'; | |
| 503 else | |
| 504 message = "Unsafe attempt to load URL " + url.elidedString() + " from fr
ame with URL " + m_document->url().elidedString() + ". Domains, protocols and po
rts must match.\n"; | |
| 505 | |
| 506 frame()->document()->addConsoleMessage(ConsoleMessage::create(SecurityMessag
eSource, ErrorMessageLevel, message)); | |
| 507 } | |
| 508 | |
| 509 void ResourceFetcher::setAutoLoadImages(bool enable) | |
| 510 { | |
| 511 if (enable == m_autoLoadImages) | |
| 512 return; | |
| 513 | |
| 514 m_autoLoadImages = enable; | |
| 515 | |
| 516 if (!m_autoLoadImages) | |
| 517 return; | |
| 518 | |
| 519 reloadImagesIfNotDeferred(); | |
| 520 } | |
| 521 | |
| 522 void ResourceFetcher::setImagesEnabled(bool enable) | |
| 523 { | |
| 524 if (enable == m_imagesEnabled) | |
| 525 return; | |
| 526 | |
| 527 m_imagesEnabled = enable; | |
| 528 | |
| 529 if (!m_imagesEnabled) | |
| 530 return; | |
| 531 | |
| 532 reloadImagesIfNotDeferred(); | |
| 533 } | |
| 534 | |
| 535 bool ResourceFetcher::clientDefersImage(const KURL& url) const | |
| 536 { | |
| 537 // FIXME(sky): remove | |
| 538 return false; | |
| 539 } | |
| 540 | |
| 541 bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const | |
| 542 { | |
| 543 return clientDefersImage(url) || !m_autoLoadImages; | |
| 544 } | |
| 545 | |
| 546 void ResourceFetcher::reloadImagesIfNotDeferred() | |
| 547 { | |
| 548 DocumentResourceMap::iterator end = m_documentResources.end(); | |
| 549 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != e
nd; ++it) { | |
| 550 Resource* resource = it->value.get(); | |
| 551 if (resource->type() == Resource::Image && resource->stillNeedsLoad() &&
!clientDefersImage(resource->url())) | |
| 552 const_cast<Resource*>(resource)->load(this, defaultResourceOptions()
); | |
| 553 } | |
| 554 } | |
| 555 | |
| 556 void ResourceFetcher::didLoadResource(Resource* resource) | |
| 557 { | |
| 558 RefPtr<Document> protectDocument(m_document); | |
| 559 if (m_document) | |
| 560 m_document->checkCompleted(); | |
| 561 scheduleDocumentResourcesGC(); | |
| 562 } | |
| 563 | |
| 564 void ResourceFetcher::scheduleDocumentResourcesGC() | |
| 565 { | |
| 566 if (!m_garbageCollectDocumentResourcesTimer.isActive()) | |
| 567 m_garbageCollectDocumentResourcesTimer.startOneShot(0, FROM_HERE); | |
| 568 } | |
| 569 | |
| 570 // Garbage collecting m_documentResources is a workaround for the | |
| 571 // ResourcePtrs on the RHS being strong references. Ideally this | |
| 572 // would be a weak map, however ResourcePtrs perform additional | |
| 573 // bookkeeping on Resources, so instead pseudo-GC them -- when the | |
| 574 // reference count reaches 1, m_documentResources is the only reference, so | |
| 575 // remove it from the map. | |
| 576 void ResourceFetcher::garbageCollectDocumentResourcesTimerFired(Timer<ResourceFe
tcher>* timer) | |
| 577 { | |
| 578 ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer); | |
| 579 garbageCollectDocumentResources(); | |
| 580 } | |
| 581 | |
| 582 void ResourceFetcher::garbageCollectDocumentResources() | |
| 583 { | |
| 584 typedef Vector<String, 10> StringVector; | |
| 585 StringVector resourcesToDelete; | |
| 586 | |
| 587 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m
_documentResources.end(); ++it) { | |
| 588 if (it->value->hasOneHandle()) | |
| 589 resourcesToDelete.append(it->key); | |
| 590 } | |
| 591 | |
| 592 m_documentResources.removeAll(resourcesToDelete); | |
| 593 } | |
| 594 | |
| 595 void ResourceFetcher::notifyLoadedFromMemoryCache(Resource* resource) | |
| 596 { | |
| 597 if (!frame() || !frame()->page() || resource->status() != Resource::Cached |
| m_validatedURLs.contains(resource->url())) | |
| 598 return; | |
| 599 | |
| 600 ResourceRequest request(resource->url()); | |
| 601 unsigned long identifier = createUniqueIdentifier(); | |
| 602 context().dispatchDidLoadResourceFromMemoryCache(request, resource->response
()); | |
| 603 // FIXME: If willSendRequest changes the request, we don't respect it. | |
| 604 willSendRequest(identifier, request, ResourceResponse(), resource->options()
.initiatorInfo); | |
| 605 context().sendRemainingDelegateMessages(m_document, identifier, resource->re
sponse(), resource->encodedSize()); | |
| 606 } | |
| 607 | |
| 608 void ResourceFetcher::incrementRequestCount(const Resource* res) | |
| 609 { | |
| 610 if (res->ignoreForRequestCount()) | |
| 611 return; | |
| 612 | |
| 613 ++m_requestCount; | |
| 614 } | |
| 615 | |
| 616 void ResourceFetcher::decrementRequestCount(const Resource* res) | |
| 617 { | |
| 618 if (res->ignoreForRequestCount()) | |
| 619 return; | |
| 620 | |
| 621 --m_requestCount; | |
| 622 ASSERT(m_requestCount > -1); | |
| 623 } | |
| 624 | |
| 625 void ResourceFetcher::didFinishLoading(const Resource* resource, double finishTi
me, int64_t encodedDataLength) | |
| 626 { | |
| 627 TRACE_EVENT_ASYNC_END0("net", "Resource", resource); | |
| 628 context().dispatchDidFinishLoading(m_document, resource->identifier(), finis
hTime, encodedDataLength); | |
| 629 } | |
| 630 | |
| 631 void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, Resourc
eLoadPriority loadPriority, int intraPriorityValue) | |
| 632 { | |
| 633 TRACE_EVENT_ASYNC_STEP_INTO1("net", "Resource", resource, "ChangePriority",
"priority", loadPriority); | |
| 634 context().dispatchDidChangeResourcePriority(resource->identifier(), loadPrio
rity, intraPriorityValue); | |
| 635 } | |
| 636 | |
| 637 void ResourceFetcher::didFailLoading(const Resource* resource, const ResourceErr
or& error) | |
| 638 { | |
| 639 TRACE_EVENT_ASYNC_END0("net", "Resource", resource); | |
| 640 context().dispatchDidFail(m_document, resource->identifier(), error); | |
| 641 } | |
| 642 | |
| 643 void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest&
request, const ResourceResponse& redirectResponse, const FetchInitiatorInfo& in
itiatorInfo) | |
| 644 { | |
| 645 context().dispatchWillSendRequest(m_document, identifier, request, redirectR
esponse, initiatorInfo); | |
| 646 } | |
| 647 | |
| 648 void ResourceFetcher::didReceiveResponse(const Resource* resource, const Resourc
eResponse& response) | |
| 649 { | |
| 650 context().dispatchDidReceiveResponse(m_document, resource->identifier(), res
ponse, resource->loader()); | |
| 651 } | |
| 652 | |
| 653 void ResourceFetcher::didReceiveData(const Resource* resource, const char* data,
int dataLength, int encodedDataLength) | |
| 654 { | |
| 655 context().dispatchDidReceiveData(m_document, resource->identifier(), data, d
ataLength, encodedDataLength); | |
| 656 } | |
| 657 | |
| 658 void ResourceFetcher::didDownloadData(const Resource* resource, int dataLength,
int encodedDataLength) | |
| 659 { | |
| 660 context().dispatchDidDownloadData(m_document, resource->identifier(), dataLe
ngth, encodedDataLength); | |
| 661 } | |
| 662 | |
| 663 void ResourceFetcher::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* lo
ader) | |
| 664 { | |
| 665 if (!m_multipartLoaders) | |
| 666 m_multipartLoaders = ResourceLoaderSet::create(); | |
| 667 m_multipartLoaders->add(loader); | |
| 668 m_loaders->remove(loader); | |
| 669 } | |
| 670 | |
| 671 void ResourceFetcher::didInitializeResourceLoader(ResourceLoader* loader) | |
| 672 { | |
| 673 if (!m_document) | |
| 674 return; | |
| 675 if (!m_loaders) | |
| 676 m_loaders = ResourceLoaderSet::create(); | |
| 677 ASSERT(!m_loaders->contains(loader)); | |
| 678 m_loaders->add(loader); | |
| 679 } | |
| 680 | |
| 681 void ResourceFetcher::willTerminateResourceLoader(ResourceLoader* loader) | |
| 682 { | |
| 683 if (m_loaders && m_loaders->contains(loader)) | |
| 684 m_loaders->remove(loader); | |
| 685 if (m_multipartLoaders && m_multipartLoaders->contains(loader)) | |
| 686 m_multipartLoaders->remove(loader); | |
| 687 } | |
| 688 | |
| 689 void ResourceFetcher::willStartLoadingResource(Resource* resource, ResourceReque
st& request) | |
| 690 { | |
| 691 TRACE_EVENT_ASYNC_BEGIN2( | |
| 692 "net", "Resource", resource, "url", | |
| 693 TRACE_STR_COPY(resource->url().string().ascii().data()), "priority", | |
| 694 resource->resourceRequest().priority()); | |
| 695 } | |
| 696 | |
| 697 void ResourceFetcher::stopFetching() | |
| 698 { | |
| 699 if (m_multipartLoaders) | |
| 700 m_multipartLoaders->cancelAll(); | |
| 701 if (m_loaders) | |
| 702 m_loaders->cancelAll(); | |
| 703 } | |
| 704 | |
| 705 bool ResourceFetcher::isFetching() const | |
| 706 { | |
| 707 return m_loaders && !m_loaders->isEmpty(); | |
| 708 } | |
| 709 | |
| 710 bool ResourceFetcher::isLoadedBy(ResourceLoaderHost* possibleOwner) const | |
| 711 { | |
| 712 return this == possibleOwner; | |
| 713 } | |
| 714 | |
| 715 #if !ENABLE(OILPAN) | |
| 716 void ResourceFetcher::refResourceLoaderHost() | |
| 717 { | |
| 718 ref(); | |
| 719 } | |
| 720 | |
| 721 void ResourceFetcher::derefResourceLoaderHost() | |
| 722 { | |
| 723 deref(); | |
| 724 } | |
| 725 #endif | |
| 726 | |
| 727 const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions() | |
| 728 { | |
| 729 DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (BufferData, AllowStored
Credentials, ClientRequestedCredentials, DocumentContext)); | |
| 730 return options; | |
| 731 } | |
| 732 | |
| 733 ResourceFetcher::DeadResourceStatsRecorder::DeadResourceStatsRecorder() | |
| 734 : m_useCount(0) | |
| 735 , m_revalidateCount(0) | |
| 736 , m_loadCount(0) | |
| 737 { | |
| 738 } | |
| 739 | |
| 740 ResourceFetcher::DeadResourceStatsRecorder::~DeadResourceStatsRecorder() | |
| 741 { | |
| 742 blink::Platform::current()->histogramCustomCounts( | |
| 743 "WebCore.ResourceFetcher.HitCount", m_useCount, 0, 1000, 50); | |
| 744 blink::Platform::current()->histogramCustomCounts( | |
| 745 "WebCore.ResourceFetcher.RevalidateCount", m_revalidateCount, 0, 1000, 5
0); | |
| 746 blink::Platform::current()->histogramCustomCounts( | |
| 747 "WebCore.ResourceFetcher.LoadCount", m_loadCount, 0, 1000, 50); | |
| 748 } | |
| 749 | |
| 750 void ResourceFetcher::DeadResourceStatsRecorder::update(RevalidationPolicy polic
y) | |
| 751 { | |
| 752 switch (policy) { | |
| 753 case Reload: | |
| 754 case Load: | |
| 755 ++m_loadCount; | |
| 756 return; | |
| 757 case Revalidate: | |
| 758 ++m_revalidateCount; | |
| 759 return; | |
| 760 case Use: | |
| 761 ++m_useCount; | |
| 762 return; | |
| 763 } | |
| 764 } | |
| 765 | |
| 766 } | |
| OLD | NEW |