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