• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
7     This library is free software; you can redistribute it and/or
8     modify it under the terms of the GNU Library General Public
9     License as published by the Free Software Foundation; either
10     version 2 of the License, or (at your option) any later version.
11 
12     This library is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15     Library General Public License for more details.
16 
17     You should have received a copy of the GNU Library General Public License
18     along with this library; see the file COPYING.LIB.  If not, write to
19     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20     Boston, MA 02110-1301, USA.
21 
22     This class provides all functionality needed for loading images, style sheets and html
23     pages from the web. It has a memory cache for these objects.
24 */
25 
26 #include "config.h"
27 #include "DocLoader.h"
28 
29 #include "Cache.h"
30 #include "CachedCSSStyleSheet.h"
31 #include "CachedFont.h"
32 #include "CachedImage.h"
33 #include "CachedScript.h"
34 #include "CachedXSLStyleSheet.h"
35 #include "Console.h"
36 #include "CString.h"
37 #include "Document.h"
38 #include "DOMWindow.h"
39 #include "Frame.h"
40 #include "FrameLoader.h"
41 #include "loader.h"
42 #include "SecurityOrigin.h"
43 #include "Settings.h"
44 
45 #define PRELOAD_DEBUG 0
46 
47 namespace WebCore {
48 
DocLoader(Document * doc)49 DocLoader::DocLoader(Document* doc)
50     : m_cache(cache())
51     , m_doc(doc)
52     , m_requestCount(0)
53 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
54     , m_blockNetworkImage(false)
55 #endif
56     , m_autoLoadImages(true)
57     , m_loadInProgress(false)
58     , m_allowStaleResources(false)
59 {
60     m_cache->addDocLoader(this);
61 }
62 
~DocLoader()63 DocLoader::~DocLoader()
64 {
65     clearPreloads();
66     DocumentResourceMap::iterator end = m_documentResources.end();
67     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it)
68         it->second->setDocLoader(0);
69     m_cache->removeDocLoader(this);
70 }
71 
frame() const72 Frame* DocLoader::frame() const
73 {
74     return m_doc->frame();
75 }
76 
checkForReload(const KURL & fullURL)77 void DocLoader::checkForReload(const KURL& fullURL)
78 {
79     if (m_allowStaleResources)
80         return; // Don't reload resources while pasting
81 
82     if (fullURL.isEmpty())
83         return;
84 
85     if (m_reloadedURLs.contains(fullURL.string()))
86         return;
87 
88     CachedResource* existing = cache()->resourceForURL(fullURL.string());
89     if (!existing || existing->isPreloaded())
90         return;
91 
92     switch (cachePolicy()) {
93     case CachePolicyVerify:
94         if (!existing->mustRevalidate(CachePolicyVerify))
95             return;
96         cache()->revalidateResource(existing, this);
97         break;
98     case CachePolicyCache:
99         if (!existing->mustRevalidate(CachePolicyCache))
100             return;
101         cache()->revalidateResource(existing, this);
102         break;
103     case CachePolicyReload:
104         cache()->remove(existing);
105         break;
106     case CachePolicyRevalidate:
107         cache()->revalidateResource(existing, this);
108         break;
109     default:
110         ASSERT_NOT_REACHED();
111     }
112 
113     m_reloadedURLs.add(fullURL.string());
114 }
115 
requestImage(const String & url)116 CachedImage* DocLoader::requestImage(const String& url)
117 {
118     CachedImage* resource = static_cast<CachedImage*>(requestResource(CachedResource::ImageResource, url, String()));
119     if (autoLoadImages() && resource && resource->stillNeedsLoad()) {
120 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
121         if (shouldBlockNetworkImage(url)) {
122             return resource;
123         }
124 #endif
125         resource->setLoading(true);
126         cache()->loader()->load(this, resource, true);
127     }
128     return resource;
129 }
130 
requestFont(const String & url)131 CachedFont* DocLoader::requestFont(const String& url)
132 {
133     return static_cast<CachedFont*>(requestResource(CachedResource::FontResource, url, String()));
134 }
135 
requestCSSStyleSheet(const String & url,const String & charset)136 CachedCSSStyleSheet* DocLoader::requestCSSStyleSheet(const String& url, const String& charset)
137 {
138     return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, url, charset));
139 }
140 
requestUserCSSStyleSheet(const String & url,const String & charset)141 CachedCSSStyleSheet* DocLoader::requestUserCSSStyleSheet(const String& url, const String& charset)
142 {
143     return cache()->requestUserCSSStyleSheet(this, url, charset);
144 }
145 
requestScript(const String & url,const String & charset)146 CachedScript* DocLoader::requestScript(const String& url, const String& charset)
147 {
148     return static_cast<CachedScript*>(requestResource(CachedResource::Script, url, charset));
149 }
150 
151 #if ENABLE(XSLT)
requestXSLStyleSheet(const String & url)152 CachedXSLStyleSheet* DocLoader::requestXSLStyleSheet(const String& url)
153 {
154     return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XSLStyleSheet, url, String()));
155 }
156 #endif
157 
158 #if ENABLE(XBL)
requestXBLDocument(const String & url)159 CachedXBLDocument* DocLoader::requestXBLDocument(const String& url)
160 {
161     return static_cast<CachedXSLStyleSheet*>(requestResource(CachedResource::XBL, url, String()));
162 }
163 #endif
164 
canRequest(CachedResource::Type type,const KURL & url)165 bool DocLoader::canRequest(CachedResource::Type type, const KURL& url)
166 {
167     // Some types of resources can be loaded only from the same origin.  Other
168     // types of resources, like Images, Scripts, and CSS, can be loaded from
169     // any URL.
170     switch (type) {
171     case CachedResource::ImageResource:
172     case CachedResource::CSSStyleSheet:
173     case CachedResource::Script:
174     case CachedResource::FontResource:
175         // These types of resources can be loaded from any origin.
176         // FIXME: Are we sure about CachedResource::FontResource?
177         break;
178 #if ENABLE(XSLT)
179     case CachedResource::XSLStyleSheet:
180 #endif
181 #if ENABLE(XBL)
182     case CachedResource::XBL:
183 #endif
184 #if ENABLE(XSLT) || ENABLE(XBL)
185         if (!m_doc->securityOrigin()->canRequest(url)) {
186             printAccessDeniedMessage(url);
187             return false;
188         }
189         break;
190 #endif
191     default:
192         ASSERT_NOT_REACHED();
193         break;
194     }
195     return true;
196 }
197 
requestResource(CachedResource::Type type,const String & url,const String & charset,bool isPreload)198 CachedResource* DocLoader::requestResource(CachedResource::Type type, const String& url, const String& charset, bool isPreload)
199 {
200     KURL fullURL = m_doc->completeURL(url);
201 
202     if (!fullURL.isValid() || !canRequest(type, fullURL))
203         return 0;
204 
205     if (cache()->disabled()) {
206         DocumentResourceMap::iterator it = m_documentResources.find(fullURL.string());
207 
208         if (it != m_documentResources.end()) {
209             it->second->setDocLoader(0);
210             m_documentResources.remove(it);
211         }
212     }
213 
214     checkForReload(fullURL);
215     CachedResource* resource = cache()->requestResource(this, type, fullURL, charset, isPreload);
216     if (resource) {
217         // Check final URL of resource to catch redirects.
218         // See <https://bugs.webkit.org/show_bug.cgi?id=21963>.
219         if (!canRequest(type, KURL(resource->url())))
220             return 0;
221 
222         m_documentResources.set(resource->url(), resource);
223         checkCacheObjectStatus(resource);
224     }
225     return resource;
226 }
227 
printAccessDeniedMessage(const KURL & url) const228 void DocLoader::printAccessDeniedMessage(const KURL& url) const
229 {
230     if (url.isNull())
231         return;
232 
233     if (!frame())
234         return;
235 
236     Settings* settings = frame()->settings();
237     if (!settings || settings->privateBrowsingEnabled())
238         return;
239 
240     String message = m_doc->url().isNull() ?
241         String::format("Unsafe attempt to load URL %s.",
242                        url.string().utf8().data()) :
243         String::format("Unsafe attempt to load URL %s from frame with URL %s. "
244                        "Domains, protocols and ports must match.\n",
245                        url.string().utf8().data(),
246                        m_doc->url().string().utf8().data());
247 
248     // FIXME: provide a real line number and source URL.
249     frame()->domWindow()->console()->addMessage(OtherMessageSource, ErrorMessageLevel, message, 1, String());
250 }
251 
setAutoLoadImages(bool enable)252 void DocLoader::setAutoLoadImages(bool enable)
253 {
254     if (enable == m_autoLoadImages)
255         return;
256 
257     m_autoLoadImages = enable;
258 
259     if (!m_autoLoadImages)
260         return;
261 
262     DocumentResourceMap::iterator end = m_documentResources.end();
263     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
264         CachedResource* resource = it->second.get();
265         if (resource->type() == CachedResource::ImageResource) {
266             CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource));
267 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
268             if (shouldBlockNetworkImage(image->url()))
269                 continue;
270 #endif
271 
272             if (image->stillNeedsLoad())
273                 cache()->loader()->load(this, image, true);
274         }
275     }
276 }
277 
278 #ifdef ANDROID_BLOCK_NETWORK_IMAGE
shouldBlockNetworkImage(const String & url) const279 bool DocLoader::shouldBlockNetworkImage(const String& url) const
280 {
281     if (!m_blockNetworkImage)
282         return false;
283 
284     KURL kurl(url);
285     if (kurl.protocolIs("http") || kurl.protocolIs("https"))
286         return true;
287 
288     return false;
289 }
290 
setBlockNetworkImage(bool block)291 void DocLoader::setBlockNetworkImage(bool block)
292 {
293     if (block == m_blockNetworkImage)
294         return;
295 
296     m_blockNetworkImage = block;
297 
298     if (!m_autoLoadImages || m_blockNetworkImage)
299         return;
300 
301     DocumentResourceMap::iterator end = m_documentResources.end();
302     for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
303         CachedResource* resource = it->second.get();
304         if (resource->type() == CachedResource::ImageResource) {
305             CachedImage* image = const_cast<CachedImage*>(static_cast<const CachedImage*>(resource));
306             if (image->stillNeedsLoad())
307                 cache()->loader()->load(this, image, true);
308         }
309     }
310 }
311 #endif
312 
cachePolicy() const313 CachePolicy DocLoader::cachePolicy() const
314 {
315     return frame() ? frame()->loader()->cachePolicy() : CachePolicyVerify;
316 }
317 
removeCachedResource(CachedResource * resource) const318 void DocLoader::removeCachedResource(CachedResource* resource) const
319 {
320     m_documentResources.remove(resource->url());
321 }
322 
setLoadInProgress(bool load)323 void DocLoader::setLoadInProgress(bool load)
324 {
325     m_loadInProgress = load;
326     if (!load && frame())
327         frame()->loader()->loadDone();
328 }
329 
checkCacheObjectStatus(CachedResource * resource)330 void DocLoader::checkCacheObjectStatus(CachedResource* resource)
331 {
332     // Return from the function for objects that we didn't load from the cache or if we don't have a frame.
333     if (!resource || !frame())
334         return;
335 
336     switch (resource->status()) {
337         case CachedResource::Cached:
338             break;
339         case CachedResource::NotCached:
340         case CachedResource::Unknown:
341         case CachedResource::New:
342         case CachedResource::Pending:
343             return;
344     }
345 
346     // FIXME: If the WebKit client changes or cancels the request, WebCore does not respect this and continues the load.
347     frame()->loader()->loadedResourceFromMemoryCache(resource);
348 }
349 
incrementRequestCount()350 void DocLoader::incrementRequestCount()
351 {
352     ++m_requestCount;
353 }
354 
decrementRequestCount()355 void DocLoader::decrementRequestCount()
356 {
357     --m_requestCount;
358     ASSERT(m_requestCount > -1);
359 }
360 
requestCount()361 int DocLoader::requestCount()
362 {
363     if (loadInProgress())
364          return m_requestCount + 1;
365     return m_requestCount;
366 }
367 
preload(CachedResource::Type type,const String & url,const String & charset,bool referencedFromBody)368 void DocLoader::preload(CachedResource::Type type, const String& url, const String& charset, bool referencedFromBody)
369 {
370     bool hasRendering = m_doc->body() && m_doc->body()->renderer();
371     if (!hasRendering && (referencedFromBody || type == CachedResource::ImageResource)) {
372         // Don't preload images or body resources before we have something to draw. This prevents
373         // preloads from body delaying first display when bandwidth is limited.
374         PendingPreload pendingPreload = { type, url, charset };
375         m_pendingPreloads.append(pendingPreload);
376         return;
377     }
378     requestPreload(type, url, charset);
379 }
380 
checkForPendingPreloads()381 void DocLoader::checkForPendingPreloads()
382 {
383     unsigned count = m_pendingPreloads.size();
384     if (!count || !m_doc->body() || !m_doc->body()->renderer())
385         return;
386     for (unsigned i = 0; i < count; ++i) {
387         PendingPreload& preload = m_pendingPreloads[i];
388         requestPreload(preload.m_type, preload.m_url, preload.m_charset);
389     }
390     m_pendingPreloads.clear();
391 }
392 
requestPreload(CachedResource::Type type,const String & url,const String & charset)393 void DocLoader::requestPreload(CachedResource::Type type, const String& url, const String& charset)
394 {
395     String encoding;
396     if (type == CachedResource::Script || type == CachedResource::CSSStyleSheet)
397         encoding = charset.isEmpty() ? m_doc->frame()->loader()->encoding() : charset;
398 
399     CachedResource* resource = requestResource(type, url, encoding, true);
400     if (!resource || m_preloads.contains(resource))
401         return;
402     resource->increasePreloadCount();
403     m_preloads.add(resource);
404 #if PRELOAD_DEBUG
405     printf("PRELOADING %s\n",  resource->url().latin1().data());
406 #endif
407 }
408 
clearPreloads()409 void DocLoader::clearPreloads()
410 {
411 #if PRELOAD_DEBUG
412     printPreloadStats();
413 #endif
414     ListHashSet<CachedResource*>::iterator end = m_preloads.end();
415     for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
416         CachedResource* res = *it;
417         res->decreasePreloadCount();
418         if (res->canDelete() && !res->inCache())
419             delete res;
420         else if (res->preloadResult() == CachedResource::PreloadNotReferenced)
421             cache()->remove(res);
422     }
423     m_preloads.clear();
424 }
425 
426 #if PRELOAD_DEBUG
printPreloadStats()427 void DocLoader::printPreloadStats()
428 {
429     unsigned scripts = 0;
430     unsigned scriptMisses = 0;
431     unsigned stylesheets = 0;
432     unsigned stylesheetMisses = 0;
433     unsigned images = 0;
434     unsigned imageMisses = 0;
435     ListHashSet<CachedResource*>::iterator end = m_preloads.end();
436     for (ListHashSet<CachedResource*>::iterator it = m_preloads.begin(); it != end; ++it) {
437         CachedResource* res = *it;
438         if (res->preloadResult() == CachedResource::PreloadNotReferenced)
439             printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
440         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileComplete)
441             printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
442         else if (res->preloadResult() == CachedResource::PreloadReferencedWhileLoading)
443             printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
444 
445         if (res->type() == CachedResource::Script) {
446             scripts++;
447             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
448                 scriptMisses++;
449         } else if (res->type() == CachedResource::CSSStyleSheet) {
450             stylesheets++;
451             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
452                 stylesheetMisses++;
453         } else {
454             images++;
455             if (res->preloadResult() < CachedResource::PreloadReferencedWhileLoading)
456                 imageMisses++;
457         }
458 
459         if (res->errorOccurred())
460             cache()->remove(res);
461 
462         res->decreasePreloadCount();
463     }
464     m_preloads.clear();
465 
466     if (scripts)
467         printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
468     if (stylesheets)
469         printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
470     if (images)
471         printf("IMAGES:  %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
472 }
473 #endif
474 
475 }
476