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