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, 2008 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 sheets and html
24 pages from the web. It has a memory cache for these objects.
25 */
26
27 #include "config.h"
28 #include "CachedResourceLoader.h"
29
30 #include "CachedCSSStyleSheet.h"
31 #include "CachedFont.h"
32 #include "CachedImage.h"
33 #include "CachedResourceRequest.h"
34 #include "CachedScript.h"
35 #include "CachedXSLStyleSheet.h"
36 #include "Console.h"
37 #include "ContentSecurityPolicy.h"
38 #include "DOMWindow.h"
39 #include "Document.h"
40 #include "Frame.h"
41 #include "FrameLoader.h"
42 #include "FrameLoaderClient.h"
43 #include "HTMLElement.h"
44 #include "Logging.h"
45 #include "MemoryCache.h"
46 #include "PingLoader.h"
47 #include "ResourceLoadScheduler.h"
48 #include "SecurityOrigin.h"
49 #include "Settings.h"
50 #include <wtf/UnusedParam.h>
51 #include <wtf/text/CString.h>
52 #include <wtf/text/StringConcatenate.h>
53
54 #define PRELOAD_DEBUG 0
55
56 namespace WebCore {
57
createResource(CachedResource::Type type,const KURL & url,const String & charset)58 static CachedResource* createResource(CachedResource::Type type, const KURL& url, const String& charset)
59 {
60 switch (type) {
61 case CachedResource::ImageResource:
62 return new CachedImage(url.string());
63 case CachedResource::CSSStyleSheet:
64 return new CachedCSSStyleSheet(url.string(), charset);
65 case CachedResource::Script:
66 return new CachedScript(url.string(), charset);
67 case CachedResource::FontResource:
68 return new CachedFont(url.string());
69 #if ENABLE(XSLT)
70 case CachedResource::XSLStyleSheet:
71 return new CachedXSLStyleSheet(url.string());
72 #endif
73 #if ENABLE(LINK_PREFETCH)
74 case CachedResource::LinkResource:
75 return new CachedResource(url.string(), CachedResource::LinkResource);
76 #endif
77 }
78 ASSERT_NOT_REACHED();
79 return 0;
80 }
81
CachedResourceLoader(Document * document)82 CachedResourceLoader::CachedResourceLoader(Document* document)
83 : m_document(document)
84 , m_requestCount(0)
85 , m_loadDoneActionTimer(this, &CachedResourceLoader::loadDoneActionTimerFired)
86 , m_autoLoadImages(true)
87 , m_loadFinishing(false)
88 , m_allowStaleResources(false)
89 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
90 , m_blockNetworkImage(false)
91 #endif
92 {
93 }
94
~CachedResourceLoader()95 CachedResourceLoader::~CachedResourceLoader()
96 {
97 m_document = 0;
98
99 cancelRequests();
100 clearPreloads();
101 DocumentResourceMap::iterator end = m_documentResources.end();
102 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
103 it->second->setOwningCachedResourceLoader(0);
104
105 // Make sure no requests still point to this CachedResourceLoader
106 ASSERT(m_requestCount == 0);
107 }
108
cachedResource(const String & resourceURL) const109 CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const
110 {
111 KURL url = m_document->completeURL(resourceURL);
112 return cachedResource(url);
113 }
114
cachedResource(const KURL & resourceURL) const115 CachedResource* CachedResourceLoader::cachedResource(const KURL& resourceURL) const
116 {
117 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
118 return m_documentResources.get(url).get();
119 }
120
frame() const121 Frame* CachedResourceLoader::frame() const
122 {
123 return m_document ? m_document->frame() : 0;
124 }
125
requestImage(const String & url)126 CachedImage* CachedResourceLoader::requestImage(const String& url)
127 {
128 if (Frame* f = frame()) {
129 Settings* settings = f->settings();
130 if (!f->loader()->client()->allowImages(!settings || settings->areImagesEnabled()))
131 return 0;
132
133 if (f->loader()->pageDismissalEventBeingDispatched()) {
134 KURL completeURL = m_document->completeURL(url);
135 if (completeURL.isValid() && canRequest(CachedResource::ImageResource, completeURL))
136 PingLoader::loadImage(f, completeURL);
137 return 0;
138 }
139 }
140 CachedImage* resource = static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, url, String()));
141 if (resource) {
142 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
143 resource->setAutoLoadWasPreventedBySettings(!autoLoadImages() || shouldBlockNetworkImage(url));
144 #else
145 resource->setAutoLoadWasPreventedBySettings(!autoLoadImages());
146 #endif
147 if (autoLoadImages() && resource->stillNeedsLoad()) {
148 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
149 if (shouldBlockNetworkImage(url)) {
150 return resource;
151 }
152 #endif
153 resource->setLoading(true);
154 load(resource, true);
155 }
156 }
157 return resource;
158 }
159
requestFont(const String & url)160 CachedFont* CachedResourceLoader::requestFont(const String& url)
161 {
162 return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url, String()));
163 }
164
requestCSSStyleSheet(const String & url,const String & charset,ResourceLoadPriority priority)165 CachedCSSStyleSheet* CachedResourceLoader::requestCSSStyleSheet(const String& url, const String& charset, ResourceLoadPriority priority)
166 {
167 return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, charset, priority));
168 }
169
requestUserCSSStyleSheet(const String & requestURL,const String & charset)170 CachedCSSStyleSheet* CachedResourceLoader::requestUserCSSStyleSheet(const String& requestURL, const String& charset)
171 {
172 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(KURL(KURL(), requestURL));
173
174 if (CachedResource* existing = memoryCache()->resourceForURL(url)) {
175 if (existing->type() == CachedResource::CSSStyleSheet)
176 return static_cast<CachedCSSStyleSheet*>(existing);
177 memoryCache()->remove(existing);
178 }
179 CachedCSSStyleSheet* userSheet = new CachedCSSStyleSheet(url, charset);
180
181 bool inCache = memoryCache()->add(userSheet);
182 if (!inCache)
183 userSheet->setInCache(true);
184
185 userSheet->load(this, /*incremental*/ false, SkipSecurityCheck, /*sendResourceLoadCallbacks*/ false);
186
187 if (!inCache)
188 userSheet->setInCache(false);
189
190 return userSheet;
191 }
192
requestScript(const String & url,const String & charset)193 CachedScript* CachedResourceLoader::requestScript(const String& url, const String& charset)
194 {
195 return static_cast<CachedScript*>(requestResource(CachedResource::Script, url, charset));
196 }
197
198 #if ENABLE(XSLT)
requestXSLStyleSheet(const String & url)199 CachedXSLStyleSheet* CachedResourceLoader::requestXSLStyleSheet(const String& url)
200 {
201 return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, url, String()));
202 }
203 #endif
204
205 #if ENABLE(LINK_PREFETCH)
requestLinkResource(const String & url,ResourceLoadPriority priority)206 CachedResource* CachedResourceLoader::requestLinkResource(const String& url, ResourceLoadPriority priority)
207 {
208 ASSERT(frame());
209 return requestResource(CachedResource::LinkResource, url, String(), priority);
210 }
211 #endif
212
canRequest(CachedResource::Type type,const KURL & url)213 bool CachedResourceLoader::canRequest(CachedResource::Type type, const KURL& url)
214 {
215 // Some types of resources can be loaded only from the same origin. Other
216 // types of resources, like Images, Scripts, and CSS, can be loaded from
217 // any URL.
218 switch (type) {
219 case CachedResource::ImageResource:
220 case CachedResource::CSSStyleSheet:
221 case CachedResource::Script:
222 case CachedResource::FontResource:
223 #if ENABLE(LINK_PREFETCH)
224 case CachedResource::LinkResource:
225 #endif
226 // These types of resources can be loaded from any origin.
227 // FIXME: Are we sure about CachedResource::FontResource?
228 break;
229 #if ENABLE(XSLT)
230 case CachedResource::XSLStyleSheet:
231 if (!m_document->securityOrigin()->canRequest(url)) {
232 printAccessDeniedMessage(url);
233 return false;
234 }
235 break;
236 #endif
237 }
238
239 // Given that the load is allowed by the same-origin policy, we should
240 // check whether the load passes the mixed-content policy.
241 //
242 // Note: Currently, we always allow mixed content, but we generate a
243 // callback to the FrameLoaderClient in case the embedder wants to
244 // update any security indicators.
245 //
246 switch (type) {
247 case CachedResource::Script:
248 #if ENABLE(XSLT)
249 case CachedResource::XSLStyleSheet:
250 #endif
251 // These resource can inject script into the current document.
252 if (Frame* f = frame())
253 f->loader()->checkIfRunInsecureContent(m_document->securityOrigin(), url);
254 break;
255 case CachedResource::ImageResource:
256 case CachedResource::CSSStyleSheet:
257 case CachedResource::FontResource: {
258 // These resources can corrupt only the frame's pixels.
259 if (Frame* f = frame()) {
260 Frame* top = f->tree()->top();
261 top->loader()->checkIfDisplayInsecureContent(top->document()->securityOrigin(), url);
262 }
263 break;
264 }
265 #if ENABLE(LINK_PREFETCH)
266 case CachedResource::LinkResource:
267 // Prefetch cannot affect the current document.
268 break;
269 #endif
270 }
271 // FIXME: Consider letting the embedder block mixed content loads.
272
273 switch (type) {
274 case CachedResource::Script:
275 if (!m_document->contentSecurityPolicy()->allowScriptFromSource(url))
276 return false;
277 break;
278 #if ENABLE(XSLT)
279 case CachedResource::XSLStyleSheet:
280 #endif
281 case CachedResource::CSSStyleSheet:
282 if (!m_document->contentSecurityPolicy()->allowStyleFromSource(url))
283 return false;
284 break;
285 case CachedResource::ImageResource:
286 if (!m_document->contentSecurityPolicy()->allowImageFromSource(url))
287 return false;
288 break;
289 case CachedResource::FontResource: {
290 if (!m_document->contentSecurityPolicy()->allowFontFromSource(url))
291 return false;
292 break;
293 }
294 #if ENABLE(LINK_PREFETCH)
295 case CachedResource::LinkResource:
296 if (!m_document->settings()->linkPrefetchEnabled())
297 return false;
298 break;
299 #endif
300 }
301
302 return true;
303 }
304
requestResource(CachedResource::Type type,const String & resourceURL,const String & charset,ResourceLoadPriority priority,bool forPreload)305 CachedResource* CachedResourceLoader::requestResource(CachedResource::Type type, const String& resourceURL, const String& charset, ResourceLoadPriority priority, bool forPreload)
306 {
307 KURL url = m_document->completeURL(resourceURL);
308
309 LOG(ResourceLoading, "CachedResourceLoader::requestResource '%s', charset '%s', priority=%d, forPreload=%u", url.string().latin1().data(), charset.latin1().data(), priority, forPreload);
310
311 // If only the fragment identifiers differ, it is the same resource.
312 url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
313
314 if (!url.isValid())
315 return 0;
316
317 if (!canRequest(type, url))
318 return 0;
319
320 // FIXME: Figure out what is the correct way to merge this security check with the one above.
321 if (!document()->securityOrigin()->canDisplay(url)) {
322 if (!forPreload)
323 FrameLoader::reportLocalLoadFailed(document()->frame(), url.string());
324 LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay");
325 return 0;
326 }
327
328 if (memoryCache()->disabled()) {
329 DocumentResourceMap::iterator it = m_documentResources.find(url.string());
330 if (it != m_documentResources.end()) {
331 it->second->setOwningCachedResourceLoader(0);
332 m_documentResources.remove(it);
333 }
334 }
335
336 // See if we can use an existing resource from the cache.
337 CachedResource* resource = memoryCache()->resourceForURL(url);
338
339 switch (determineRevalidationPolicy(type, forPreload, resource)) {
340 case Load:
341 resource = loadResource(type, url, charset, priority);
342 break;
343 case Reload:
344 memoryCache()->remove(resource);
345 resource = loadResource(type, url, charset, priority);
346 break;
347 case Revalidate:
348 resource = revalidateResource(resource, priority);
349 break;
350 case Use:
351 memoryCache()->resourceAccessed(resource);
352 notifyLoadedFromMemoryCache(resource);
353 break;
354 }
355
356 if (!resource)
357 return 0;
358
359 ASSERT(resource->url() == url.string());
360 m_documentResources.set(resource->url(), resource);
361
362 return resource;
363 }
364
revalidateResource(CachedResource * resource,ResourceLoadPriority priority)365 CachedResource* CachedResourceLoader::revalidateResource(CachedResource* resource, ResourceLoadPriority priority)
366 {
367 ASSERT(resource);
368 ASSERT(resource->inCache());
369 ASSERT(!memoryCache()->disabled());
370 ASSERT(resource->canUseCacheValidator());
371 ASSERT(!resource->resourceToRevalidate());
372
373 // Copy the URL out of the resource to be revalidated in case it gets deleted by the remove() call below.
374 String url = resource->url();
375 CachedResource* newResource = createResource(resource->type(), KURL(ParsedURLString, url), resource->encoding());
376
377 LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource, resource);
378 newResource->setResourceToRevalidate(resource);
379
380 memoryCache()->remove(resource);
381 memoryCache()->add(newResource);
382
383 newResource->setLoadPriority(priority);
384 newResource->load(this);
385
386 m_validatedURLs.add(url);
387 return newResource;
388 }
389
loadResource(CachedResource::Type type,const KURL & url,const String & charset,ResourceLoadPriority priority)390 CachedResource* CachedResourceLoader::loadResource(CachedResource::Type type, const KURL& url, const String& charset, ResourceLoadPriority priority)
391 {
392 ASSERT(!memoryCache()->resourceForURL(url));
393
394 LOG(ResourceLoading, "Loading CachedResource for '%s'.", url.string().latin1().data());
395
396 CachedResource* resource = createResource(type, url, charset);
397
398 bool inCache = memoryCache()->add(resource);
399
400 // Pretend the resource is in the cache, to prevent it from being deleted during the load() call.
401 // FIXME: CachedResource should just use normal refcounting instead.
402 if (!inCache)
403 resource->setInCache(true);
404
405 resource->setLoadPriority(priority);
406 resource->load(this);
407
408 if (!inCache) {
409 resource->setOwningCachedResourceLoader(this);
410 resource->setInCache(false);
411 }
412
413 // We don't support immediate loads, but we do support immediate failure.
414 if (resource->errorOccurred()) {
415 if (inCache)
416 memoryCache()->remove(resource);
417 else
418 delete resource;
419 return 0;
420 }
421
422 m_validatedURLs.add(url.string());
423 return resource;
424 }
425
determineRevalidationPolicy(CachedResource::Type type,bool forPreload,CachedResource * existingResource) const426 CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, bool forPreload, CachedResource* existingResource) const
427 {
428 if (!existingResource)
429 return Load;
430
431 // We already have a preload going for this URL.
432 if (forPreload && existingResource->isPreloaded())
433 return Use;
434
435 // If the same URL has been loaded as a different type, we need to reload.
436 if (existingResource->type() != type) {
437 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch.");
438 return Reload;
439 }
440
441 // Don't reload resources while pasting.
442 if (m_allowStaleResources)
443 return Use;
444
445 // Alwaus use preloads.
446 if (existingResource->isPreloaded())
447 return Use;
448
449 // CachePolicyHistoryBuffer uses the cache no matter what.
450 if (cachePolicy() == CachePolicyHistoryBuffer)
451 return Use;
452
453 // Don't reuse resources with Cache-control: no-store.
454 if (existingResource->response().cacheControlContainsNoStore()) {
455 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store.");
456 return Reload;
457 }
458
459 // Avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to.
460 if (m_validatedURLs.contains(existingResource->url()))
461 return Use;
462
463 // CachePolicyReload always reloads
464 if (cachePolicy() == CachePolicyReload) {
465 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.");
466 return Reload;
467 }
468
469 // We'll try to reload the resource if it failed last time.
470 if (existingResource->errorOccurred()) {
471 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state");
472 return Reload;
473 }
474
475 // For resources that are not yet loaded we ignore the cache policy.
476 if (existingResource->isLoading())
477 return Use;
478
479 // Check if the cache headers requires us to revalidate (cache expiration for example).
480 if (existingResource->mustRevalidateDueToCacheHeaders(cachePolicy())) {
481 // See if the resource has usable ETag or Last-modified headers.
482 if (existingResource->canUseCacheValidator())
483 return Revalidate;
484
485 // No, must reload.
486 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.");
487 return Reload;
488 }
489
490 return Use;
491 }
492
printAccessDeniedMessage(const KURL & url) const493 void CachedResourceLoader::printAccessDeniedMessage(const KURL& url) const
494 {
495 if (url.isNull())
496 return;
497
498 if (!frame())
499 return;
500
501 Settings* settings = frame()->settings();
502 if (!settings || settings->privateBrowsingEnabled())
503 return;
504
505 String message = m_document->url().isNull() ?
506 makeString("Unsafe attempt to load URL ", url.string(), '.') :
507 makeString("Unsafe attempt to load URL ", url.string(), " from frame with URL ", m_document->url().string(), ". Domains, protocols and ports must match.\n");
508
509 // FIXME: provide a real line number and source URL.
510 frame()->domWindow()->console()->addMessage(OtherMessageSource, LogMessageType, ErrorMessageLevel, message, 1, String());
511 }
512
setAutoLoadImages(bool enable)513 void CachedResourceLoader::setAutoLoadImages(bool enable)
514 {
515 if (enable == m_autoLoadImages)
516 return;
517
518 m_autoLoadImages = enable;
519
520 if (!m_autoLoadImages)
521 return;
522
523 DocumentResourceMap::iterator end = m_documentResources.end();
524 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
525 CachedResource* resource = it->second.get();
526 if (resource->type() == CachedResource::ImageResource) {
527 CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource));
528 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
529 if (shouldBlockNetworkImage(image->url()))
530 continue;
531 #endif
532 image->setAutoLoadWasPreventedBySettings(false);
533
534 if (image->stillNeedsLoad()) {
535 image->setLoading(true);
536 load(image, true);
537 }
538 }
539 }
540 }
541
542 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
shouldBlockNetworkImage(const String & url) const543 bool CachedResourceLoader::shouldBlockNetworkImage(const String& url) const
544 {
545 if (!m_blockNetworkImage)
546 return false;
547
548 KURL kurl = m_document->completeURL(url);
549 if (kurl.protocolIs("http") || kurl.protocolIs("https"))
550 return true;
551 return false;
552 }
553
setBlockNetworkImage(bool block)554 void CachedResourceLoader::setBlockNetworkImage(bool block)
555 {
556 if (block == m_blockNetworkImage)
557 return;
558
559 m_blockNetworkImage = block;
560
561 if (!m_autoLoadImages || m_blockNetworkImage)
562 return;
563
564 DocumentResourceMap::iterator end = m_documentResources.end();
565 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
566 CachedResource* resource = it->second.get();
567 if (resource->type() == CachedResource::ImageResource) {
568 CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource));
569 image->setAutoLoadWasPreventedBySettings(false);
570 if (image->stillNeedsLoad()) {
571 image->setLoading(true);
572 load(image, true);
573 }
574 }
575 }
576 }
577 #endif
578
cachePolicy() const579 CachePolicy CachedResourceLoader::cachePolicy() const
580 {
581 return frame() ? frame()->loader()->subresourceCachePolicy() : CachePolicyVerify;
582 }
583
removeCachedResource(CachedResource * resource) const584 void CachedResourceLoader::removeCachedResource(CachedResource* resource) const
585 {
586 #ifndef NDEBUG
587 DocumentResourceMap::iterator it = m_documentResources.find(resource->url());
588 if (it != m_documentResources.end())
589 ASSERT(it->second.get() == resource);
590 #endif
591 m_documentResources.remove(resource->url());
592 }
593
load(CachedResource * resource,bool incremental,SecurityCheckPolicy securityCheck,bool sendResourceLoadCallbacks)594 void CachedResourceLoader::load(CachedResource* resource, bool incremental, SecurityCheckPolicy securityCheck, bool sendResourceLoadCallbacks)
595 {
596 incrementRequestCount(resource);
597
598 RefPtr<CachedResourceRequest> request = CachedResourceRequest::load(this, resource, incremental, securityCheck, sendResourceLoadCallbacks);
599 if (request)
600 m_requests.add(request);
601 }
602
loadDone(CachedResourceRequest * request)603 void CachedResourceLoader::loadDone(CachedResourceRequest* request)
604 {
605 m_loadFinishing = false;
606 RefPtr<CachedResourceRequest> protect(request);
607 if (request)
608 m_requests.remove(request);
609 if (frame())
610 frame()->loader()->loadDone();
611
612 if (!request) {
613 // If the request passed to this function is null, loadDone finished synchronously from when
614 // the load was started, so we want to kick off our next set of loads (via checkForPendingPreloads
615 // and servePendingRequests) asynchronously.
616 m_loadDoneActionTimer.startOneShot(0);
617 return;
618 }
619
620 performPostLoadActions();
621 }
622
loadDoneActionTimerFired(Timer<CachedResourceLoader> *)623 void CachedResourceLoader::loadDoneActionTimerFired(Timer<CachedResourceLoader>*)
624 {
625 performPostLoadActions();
626 }
627
performPostLoadActions()628 void CachedResourceLoader::performPostLoadActions()
629 {
630 checkForPendingPreloads();
631 resourceLoadScheduler()->servePendingRequests();
632 }
633
cancelRequests()634 void CachedResourceLoader::cancelRequests()
635 {
636 clearPendingPreloads();
637 Vector<CachedResourceRequest*, 256> requestsToCancel;
638 RequestSet::iterator end = m_requests.end();
639 for (RequestSet::iterator i = m_requests.begin(); i != end; ++i)
640 requestsToCancel.append((*i).get());
641
642 for (unsigned i = 0; i < requestsToCancel.size(); ++i)
643 requestsToCancel[i]->didFail(true);
644 }
645
notifyLoadedFromMemoryCache(CachedResource * resource)646 void CachedResourceLoader::notifyLoadedFromMemoryCache(CachedResource* resource)
647 {
648 if (!resource || !frame() || resource->status() != CachedResource::Cached)
649 return;
650
651 // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load.
652 frame()->loader()->loadedResourceFromMemoryCache(resource);
653 }
654
incrementRequestCount(const CachedResource * res)655 void CachedResourceLoader::incrementRequestCount(const CachedResource* res)
656 {
657 if (res->isLinkResource())
658 return;
659
660 ++m_requestCount;
661 }
662
decrementRequestCount(const CachedResource * res)663 void CachedResourceLoader::decrementRequestCount(const CachedResource* res)
664 {
665 if (res->isLinkResource())
666 return;
667
668 --m_requestCount;
669 ASSERT(m_requestCount > -1);
670 }
671
requestCount()672 int CachedResourceLoader::requestCount()
673 {
674 if (m_loadFinishing)
675 return m_requestCount + 1;
676 return m_requestCount;
677 }
678
preload(CachedResource::Type type,const String & url,const String & charset,bool referencedFromBody)679 void CachedResourceLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool referencedFromBody)
680 {
681 // FIXME: Rip this out when we are sure it is no longer necessary (even for mobile).
682 UNUSED_PARAM(referencedFromBody);
683
684 bool hasRendering = m_document->body() && m_document->body()->renderer();
685 bool canBlockParser = type == CachedResource::Script || type == CachedResource::CSSStyleSheet;
686 if (!hasRendering && !canBlockParser) {
687 // Don't preload subresources that can't block the parser before we have something to draw.
688 // This helps prevent preloads from delaying first display when bandwidth is limited.
689 PendingPreload pendingPreload = { type, url, charset };
690 m_pendingPreloads.append(pendingPreload);
691 return;
692 }
693 requestPreload(type, url, charset);
694 }
695
checkForPendingPreloads()696 void CachedResourceLoader::checkForPendingPreloads()
697 {
698 if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
699 return;
700 while (!m_pendingPreloads.isEmpty()) {
701 PendingPreload preload = m_pendingPreloads.takeFirst();
702 // Don't request preload if the resource already loaded normally (this will result in double load if the page is being reloaded with cached results ignored).
703 if (!cachedResource(m_document->completeURL(preload.m_url)))
704 requestPreload(preload.m_type, preload.m_url, preload.m_charset);
705 }
706 m_pendingPreloads.clear();
707 }
708
requestPreload(CachedResource::Type type,const String & url,const String & charset)709 void CachedResourceLoader::requestPreload(CachedResource::Type type, const String& url, const String& charset)
710 {
711 String encoding;
712 if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
713 encoding = charset.isEmpty() ? m_document->charset() : charset;
714
715 CachedResource* resource = requestResource(type, url, encoding, ResourceLoadPriorityUnresolved, true);
716 if (!resource || (m_preloads && m_preloads->contains(resource)))
717 return;
718 resource->increasePreloadCount();
719
720 if (!m_preloads)
721 m_preloads = adoptPtr(new ListHashSet<CachedResource*>);
722 m_preloads->add(resource);
723
724 #if PRELOAD_DEBUG
725 printf("PRELOADING %s\n", resource->url().latin1().data());
726 #endif
727 }
728
clearPreloads()729 void CachedResourceLoader::clearPreloads()
730 {
731 #if PRELOAD_DEBUG
732 printPreloadStats();
733 #endif
734 if (!m_preloads)
735 return;
736
737 ListHashSet<CachedResource*>::iterator end = m_preloads->end();
738 for (ListHashSet<CachedResource*>::iterator it = m_preloads->begin(); it != end; ++it) {
739 CachedResource* res = *it;
740 res->decreasePreloadCount();
741 if (res->canDelete() && !res->inCache())
742 delete res;
743 else if (res->preloadResult() == CachedResource::PreloadNotReferenced)
744 memoryCache()->remove(res);
745 }
746 m_preloads.clear();
747 }
748
clearPendingPreloads()749 void CachedResourceLoader::clearPendingPreloads()
750 {
751 m_pendingPreloads.clear();
752 }
753
754 #if PRELOAD_DEBUG
printPreloadStats()755 void CachedResourceLoader::printPreloadStats()
756 {
757 unsigned scripts = 0;
758 unsigned scriptMisses = 0;
759 unsigned stylesheets = 0;
760 unsigned stylesheetMisses = 0;
761 unsigned images = 0;
762 unsigned imageMisses = 0;
763 ListHashSet<CachedResource*>::iterator end = m_preloads.end();
764 for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
765 CachedResource* res = *it;
766 if (res->preloadResult() == CachedResource::PreloadNotReferenced)
767 printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
768 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
769 printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
770 else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
771 printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
772
773 if (res->type() == CachedResource::Script) {
774 scripts++;
775 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
776 scriptMisses++;
777 } else if (res->type() == CachedResource::CSSStyleSheet) {
778 stylesheets++;
779 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
780 stylesheetMisses++;
781 } else {
782 images++;
783 if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
784 imageMisses++;
785 }
786
787 if (res->errorOccurred())
788 memoryCache()->remove(res);
789
790 res->decreasePreloadCount();
791 }
792 m_preloads.clear();
793
794 if (scripts)
795 printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
796 if (stylesheets)
797 printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
798 if (images)
799 printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
800 }
801 #endif
802
803 }
804