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