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, 2007, 2008, 2009, 2010, 2011 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 "core/fetch/ResourceFetcher.h"
29
30 #include "RuntimeEnabledFeatures.h"
31 #include "bindings/v8/ScriptController.h"
32 #include "core/dom/Document.h"
33 #include "core/fetch/CSSStyleSheetResource.h"
34 #include "core/fetch/DocumentResource.h"
35 #include "core/fetch/FetchContext.h"
36 #include "core/fetch/FontResource.h"
37 #include "core/fetch/ImageResource.h"
38 #include "core/fetch/MemoryCache.h"
39 #include "core/fetch/RawResource.h"
40 #include "core/fetch/ResourceLoader.h"
41 #include "core/fetch/ResourceLoaderSet.h"
42 #include "core/fetch/ScriptResource.h"
43 #include "core/fetch/ShaderResource.h"
44 #include "core/fetch/XSLStyleSheetResource.h"
45 #include "core/html/HTMLElement.h"
46 #include "core/html/HTMLFrameOwnerElement.h"
47 #include "core/html/HTMLImport.h"
48 #include "core/inspector/InspectorInstrumentation.h"
49 #include "core/loader/DocumentLoader.h"
50 #include "core/loader/FrameLoader.h"
51 #include "core/loader/FrameLoaderClient.h"
52 #include "core/loader/PingLoader.h"
53 #include "core/loader/UniqueIdentifier.h"
54 #include "core/loader/appcache/ApplicationCacheHost.h"
55 #include "core/frame/ContentSecurityPolicy.h"
56 #include "core/frame/DOMWindow.h"
57 #include "core/frame/Frame.h"
58 #include "core/timing/Performance.h"
59 #include "core/timing/ResourceTimingInfo.h"
60 #include "core/frame/Settings.h"
61 #include "platform/Logging.h"
62 #include "platform/TraceEvent.h"
63 #include "platform/weborigin/SecurityOrigin.h"
64 #include "platform/weborigin/SecurityPolicy.h"
65 #include "public/platform/Platform.h"
66 #include "public/platform/WebURL.h"
67 #include "wtf/text/CString.h"
68 #include "wtf/text/WTFString.h"
69
70 #define PRELOAD_DEBUG 0
71
72 namespace WebCore {
73
createResource(Resource::Type type,const ResourceRequest & request,const String & charset)74 static Resource* createResource(Resource::Type type, const ResourceRequest& request, const String& charset)
75 {
76 switch (type) {
77 case Resource::Image:
78 return new ImageResource(request);
79 case Resource::CSSStyleSheet:
80 return new CSSStyleSheetResource(request, charset);
81 case Resource::Script:
82 return new ScriptResource(request, charset);
83 case Resource::SVGDocument:
84 return new DocumentResource(request, Resource::SVGDocument);
85 case Resource::Font:
86 return new FontResource(request);
87 case Resource::MainResource:
88 case Resource::Raw:
89 case Resource::TextTrack:
90 return new RawResource(request, type);
91 case Resource::XSLStyleSheet:
92 return new XSLStyleSheetResource(request);
93 case Resource::LinkPrefetch:
94 return new Resource(request, Resource::LinkPrefetch);
95 case Resource::LinkSubresource:
96 return new Resource(request, Resource::LinkSubresource);
97 case Resource::Shader:
98 return new ShaderResource(request);
99 case Resource::ImportResource:
100 return new RawResource(request, type);
101 }
102
103 ASSERT_NOT_REACHED();
104 return 0;
105 }
106
loadPriority(Resource::Type type,const FetchRequest & request)107 static ResourceLoadPriority loadPriority(Resource::Type type, const FetchRequest& request)
108 {
109 if (request.priority() != ResourceLoadPriorityUnresolved)
110 return request.priority();
111
112 switch (type) {
113 case Resource::MainResource:
114 return ResourceLoadPriorityVeryHigh;
115 case Resource::CSSStyleSheet:
116 return ResourceLoadPriorityHigh;
117 case Resource::Raw:
118 return request.options().synchronousPolicy == RequestSynchronously ? ResourceLoadPriorityVeryHigh : ResourceLoadPriorityMedium;
119 case Resource::Script:
120 case Resource::Font:
121 case Resource::ImportResource:
122 return ResourceLoadPriorityMedium;
123 case Resource::Image:
124 // We'll default images to VeryLow, and promote whatever is visible. This improves
125 // speed-index by ~5% on average, ~14% at the 99th percentile.
126 return ResourceLoadPriorityVeryLow;
127 case Resource::XSLStyleSheet:
128 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
129 return ResourceLoadPriorityHigh;
130 case Resource::SVGDocument:
131 return ResourceLoadPriorityLow;
132 case Resource::LinkPrefetch:
133 return ResourceLoadPriorityVeryLow;
134 case Resource::LinkSubresource:
135 return ResourceLoadPriorityLow;
136 case Resource::TextTrack:
137 return ResourceLoadPriorityLow;
138 case Resource::Shader:
139 return ResourceLoadPriorityMedium;
140 }
141 ASSERT_NOT_REACHED();
142 return ResourceLoadPriorityUnresolved;
143 }
144
resourceFromDataURIRequest(const ResourceRequest & request,const ResourceLoaderOptions & resourceOptions)145 static Resource* resourceFromDataURIRequest(const ResourceRequest& request, const ResourceLoaderOptions& resourceOptions)
146 {
147 const KURL& url = request.url();
148 ASSERT(url.protocolIsData());
149
150 blink::WebString mimetype;
151 blink::WebString charset;
152 RefPtr<SharedBuffer> data = PassRefPtr<SharedBuffer>(blink::Platform::current()->parseDataURL(url, mimetype, charset));
153 if (!data)
154 return 0;
155 ResourceResponse response(url, mimetype, data->size(), charset, String());
156
157 Resource* resource = createResource(Resource::Image, request, charset);
158 resource->setOptions(resourceOptions);
159 resource->responseReceived(response);
160 // FIXME: AppendData causes an unnecessary memcpy.
161 if (data->size())
162 resource->appendData(data->data(), data->size());
163 resource->finish();
164 return resource;
165 }
166
populateResourceTiming(ResourceTimingInfo * info,Resource * resource,bool clearLoadTimings)167 static void populateResourceTiming(ResourceTimingInfo* info, Resource* resource, bool clearLoadTimings)
168 {
169 info->setInitialRequest(resource->resourceRequest());
170 info->setFinalResponse(resource->response());
171 if (clearLoadTimings)
172 info->clearLoadTimings();
173 info->setLoadFinishTime(resource->loadFinishTime());
174 }
175
reportResourceTiming(ResourceTimingInfo * info,Document * initiatorDocument,bool isMainResource)176 static void reportResourceTiming(ResourceTimingInfo* info, Document* initiatorDocument, bool isMainResource)
177 {
178 if (initiatorDocument && isMainResource)
179 initiatorDocument = initiatorDocument->parentDocument();
180 if (!initiatorDocument || !initiatorDocument->loader())
181 return;
182 if (DOMWindow* initiatorWindow = initiatorDocument->domWindow()) {
183 if (Performance* performance = initiatorWindow->performance())
184 performance->addResourceTiming(*info, initiatorDocument);
185 }
186 }
187
requestTargetType(const ResourceFetcher * fetcher,const ResourceRequest & request,Resource::Type type)188 static ResourceRequest::TargetType requestTargetType(const ResourceFetcher* fetcher, const ResourceRequest& request, Resource::Type type)
189 {
190 switch (type) {
191 case Resource::MainResource:
192 if (fetcher->frame()->tree().parent())
193 return ResourceRequest::TargetIsSubframe;
194 return ResourceRequest::TargetIsMainFrame;
195 case Resource::XSLStyleSheet:
196 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
197 case Resource::CSSStyleSheet:
198 return ResourceRequest::TargetIsStyleSheet;
199 case Resource::Script:
200 return ResourceRequest::TargetIsScript;
201 case Resource::Font:
202 return ResourceRequest::TargetIsFont;
203 case Resource::Image:
204 return ResourceRequest::TargetIsImage;
205 case Resource::Shader:
206 case Resource::Raw:
207 case Resource::ImportResource:
208 return ResourceRequest::TargetIsSubresource;
209 case Resource::LinkPrefetch:
210 return ResourceRequest::TargetIsPrefetch;
211 case Resource::LinkSubresource:
212 return ResourceRequest::TargetIsSubresource;
213 case Resource::TextTrack:
214 return ResourceRequest::TargetIsTextTrack;
215 case Resource::SVGDocument:
216 return ResourceRequest::TargetIsImage;
217 }
218 ASSERT_NOT_REACHED();
219 return ResourceRequest::TargetIsSubresource;
220 }
221
ResourceFetcher(DocumentLoader * documentLoader)222 ResourceFetcher::ResourceFetcher(DocumentLoader* documentLoader)
223 : m_document(0)
224 , m_documentLoader(documentLoader)
225 , m_requestCount(0)
226 , m_garbageCollectDocumentResourcesTimer(this, &ResourceFetcher::garbageCollectDocumentResourcesTimerFired)
227 , m_resourceTimingReportTimer(this, &ResourceFetcher::resourceTimingReportTimerFired)
228 , m_autoLoadImages(true)
229 , m_imagesEnabled(true)
230 , m_allowStaleResources(false)
231 {
232 }
233
~ResourceFetcher()234 ResourceFetcher::~ResourceFetcher()
235 {
236 m_documentLoader = 0;
237 m_document = 0;
238
239 clearPreloads();
240
241 // Make sure no requests still point to this ResourceFetcher
242 ASSERT(!m_requestCount);
243 }
244
cachedResource(const String & resourceURL) const245 Resource* ResourceFetcher::cachedResource(const String& resourceURL) const
246 {
247 KURL url = m_document->completeURL(resourceURL);
248 return cachedResource(url);
249 }
250
cachedResource(const KURL & resourceURL) const251 Resource* ResourceFetcher::cachedResource(const KURL& resourceURL) const
252 {
253 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(resourceURL);
254 return m_documentResources.get(url).get();
255 }
256
frame() const257 Frame* ResourceFetcher::frame() const
258 {
259 if (m_documentLoader)
260 return m_documentLoader->frame();
261 if (m_document && m_document->import())
262 return m_document->import()->frame();
263 return 0;
264 }
265
context() const266 FetchContext& ResourceFetcher::context() const
267 {
268 if (Frame* frame = this->frame())
269 return frame->fetchContext();
270 return FetchContext::nullInstance();
271 }
272
fetchSynchronously(FetchRequest & request)273 ResourcePtr<Resource> ResourceFetcher::fetchSynchronously(FetchRequest& request)
274 {
275 ASSERT(document());
276 request.mutableResourceRequest().setTimeoutInterval(10);
277 ResourceLoaderOptions options(request.options());
278 options.synchronousPolicy = RequestSynchronously;
279 request.setOptions(options);
280 return requestResource(Resource::Raw, request);
281 }
282
fetchImage(FetchRequest & request)283 ResourcePtr<ImageResource> ResourceFetcher::fetchImage(FetchRequest& request)
284 {
285 if (Frame* f = frame()) {
286 if (f->document()->pageDismissalEventBeingDispatched() != Document::NoDismissal) {
287 KURL requestURL = request.resourceRequest().url();
288 if (requestURL.isValid() && canRequest(Resource::Image, requestURL, request.options(), request.forPreload(), request.originRestriction()))
289 PingLoader::loadImage(f, requestURL);
290 return 0;
291 }
292 }
293
294 if (request.resourceRequest().url().protocolIsData())
295 preCacheDataURIImage(request);
296
297 request.setDefer(clientDefersImage(request.resourceRequest().url()) ? FetchRequest::DeferredByClient : FetchRequest::NoDefer);
298 return toImageResource(requestResource(Resource::Image, request));
299 }
300
preCacheDataURIImage(const FetchRequest & request)301 void ResourceFetcher::preCacheDataURIImage(const FetchRequest& request)
302 {
303 const KURL& url = request.resourceRequest().url();
304 ASSERT(url.protocolIsData());
305
306 if (memoryCache()->resourceForURL(url))
307 return;
308
309 if (Resource* resource = resourceFromDataURIRequest(request.resourceRequest(), request.options()))
310 memoryCache()->add(resource);
311 }
312
fetchFont(FetchRequest & request)313 ResourcePtr<FontResource> ResourceFetcher::fetchFont(FetchRequest& request)
314 {
315 return toFontResource(requestResource(Resource::Font, request));
316 }
317
fetchShader(FetchRequest & request)318 ResourcePtr<ShaderResource> ResourceFetcher::fetchShader(FetchRequest& request)
319 {
320 return toShaderResource(requestResource(Resource::Shader, request));
321 }
322
fetchImport(FetchRequest & request)323 ResourcePtr<RawResource> ResourceFetcher::fetchImport(FetchRequest& request)
324 {
325 return toRawResource(requestResource(Resource::ImportResource, request));
326 }
327
fetchCSSStyleSheet(FetchRequest & request)328 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchCSSStyleSheet(FetchRequest& request)
329 {
330 return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request));
331 }
332
fetchUserCSSStyleSheet(FetchRequest & request)333 ResourcePtr<CSSStyleSheetResource> ResourceFetcher::fetchUserCSSStyleSheet(FetchRequest& request)
334 {
335 KURL url = MemoryCache::removeFragmentIdentifierIfNeeded(request.resourceRequest().url());
336
337 if (Resource* existing = memoryCache()->resourceForURL(url)) {
338 if (existing->type() == Resource::CSSStyleSheet)
339 return toCSSStyleSheetResource(existing);
340 memoryCache()->remove(existing);
341 }
342
343 request.setOptions(ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck, CheckContentSecurityPolicy, DocumentContext));
344 return toCSSStyleSheetResource(requestResource(Resource::CSSStyleSheet, request));
345 }
346
fetchScript(FetchRequest & request)347 ResourcePtr<ScriptResource> ResourceFetcher::fetchScript(FetchRequest& request)
348 {
349 return toScriptResource(requestResource(Resource::Script, request));
350 }
351
fetchXSLStyleSheet(FetchRequest & request)352 ResourcePtr<XSLStyleSheetResource> ResourceFetcher::fetchXSLStyleSheet(FetchRequest& request)
353 {
354 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
355 return toXSLStyleSheetResource(requestResource(Resource::XSLStyleSheet, request));
356 }
357
fetchSVGDocument(FetchRequest & request)358 ResourcePtr<DocumentResource> ResourceFetcher::fetchSVGDocument(FetchRequest& request)
359 {
360 return toDocumentResource(requestResource(Resource::SVGDocument, request));
361 }
362
fetchLinkResource(Resource::Type type,FetchRequest & request)363 ResourcePtr<Resource> ResourceFetcher::fetchLinkResource(Resource::Type type, FetchRequest& request)
364 {
365 ASSERT(frame());
366 ASSERT(type == Resource::LinkPrefetch || type == Resource::LinkSubresource);
367 return requestResource(type, request);
368 }
369
fetchRawResource(FetchRequest & request)370 ResourcePtr<RawResource> ResourceFetcher::fetchRawResource(FetchRequest& request)
371 {
372 return toRawResource(requestResource(Resource::Raw, request));
373 }
374
fetchMainResource(FetchRequest & request)375 ResourcePtr<RawResource> ResourceFetcher::fetchMainResource(FetchRequest& request)
376 {
377 return toRawResource(requestResource(Resource::MainResource, request));
378 }
379
checkInsecureContent(Resource::Type type,const KURL & url,MixedContentBlockingTreatment treatment) const380 bool ResourceFetcher::checkInsecureContent(Resource::Type type, const KURL& url, MixedContentBlockingTreatment treatment) const
381 {
382 if (treatment == TreatAsDefaultForType) {
383 switch (type) {
384 case Resource::XSLStyleSheet:
385 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
386 case Resource::Script:
387 case Resource::SVGDocument:
388 case Resource::CSSStyleSheet:
389 case Resource::ImportResource:
390 // These resource can inject script into the current document (Script,
391 // XSL) or exfiltrate the content of the current document (CSS).
392 treatment = TreatAsActiveContent;
393 break;
394
395 case Resource::TextTrack:
396 case Resource::Shader:
397 case Resource::Raw:
398 case Resource::Image:
399 case Resource::Font:
400 // These resources can corrupt only the frame's pixels.
401 treatment = TreatAsPassiveContent;
402 break;
403
404 case Resource::MainResource:
405 case Resource::LinkPrefetch:
406 case Resource::LinkSubresource:
407 // These cannot affect the current document.
408 treatment = TreatAsAlwaysAllowedContent;
409 break;
410 }
411 }
412 if (treatment == TreatAsActiveContent) {
413 if (Frame* f = frame()) {
414 if (!f->loader().mixedContentChecker()->canRunInsecureContent(m_document->securityOrigin(), url))
415 return false;
416 Frame* top = f->tree().top();
417 if (top != f && !top->loader().mixedContentChecker()->canRunInsecureContent(top->document()->securityOrigin(), url))
418 return false;
419 }
420 } else if (treatment == TreatAsPassiveContent) {
421 if (Frame* f = frame()) {
422 Frame* top = f->tree().top();
423 if (!top->loader().mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), url))
424 return false;
425 }
426 } else {
427 ASSERT(treatment == TreatAsAlwaysAllowedContent);
428 }
429 return true;
430 }
431
canRequest(Resource::Type type,const KURL & url,const ResourceLoaderOptions & options,bool forPreload,FetchRequest::OriginRestriction originRestriction)432 bool ResourceFetcher::canRequest(Resource::Type type, const KURL& url, const ResourceLoaderOptions& options, bool forPreload, FetchRequest::OriginRestriction originRestriction)
433 {
434 SecurityOrigin* securityOrigin = options.securityOrigin.get();
435 if (!securityOrigin && document())
436 securityOrigin = document()->securityOrigin();
437
438 if (securityOrigin && !securityOrigin->canDisplay(url)) {
439 if (!forPreload)
440 context().reportLocalLoadFailed(url);
441 WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource URL was not allowed by SecurityOrigin::canDisplay");
442 return 0;
443 }
444
445 // FIXME: Convert this to check the isolated world's Content Security Policy once webkit.org/b/104520 is solved.
446 bool shouldBypassMainWorldContentSecurityPolicy = (frame() && frame()->script().shouldBypassMainWorldContentSecurityPolicy()) || (options.contentSecurityPolicyOption == DoNotCheckContentSecurityPolicy);
447
448 // Some types of resources can be loaded only from the same origin. Other
449 // types of resources, like Images, Scripts, and CSS, can be loaded from
450 // any URL.
451 switch (type) {
452 case Resource::MainResource:
453 case Resource::Image:
454 case Resource::CSSStyleSheet:
455 case Resource::Script:
456 case Resource::Font:
457 case Resource::Raw:
458 case Resource::LinkPrefetch:
459 case Resource::LinkSubresource:
460 case Resource::TextTrack:
461 case Resource::Shader:
462 case Resource::ImportResource:
463 // By default these types of resources can be loaded from any origin.
464 // FIXME: Are we sure about Resource::Font?
465 if (originRestriction == FetchRequest::RestrictToSameOrigin && !securityOrigin->canRequest(url)) {
466 printAccessDeniedMessage(url);
467 return false;
468 }
469 break;
470 case Resource::XSLStyleSheet:
471 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
472 case Resource::SVGDocument:
473 if (!securityOrigin->canRequest(url)) {
474 printAccessDeniedMessage(url);
475 return false;
476 }
477 break;
478 }
479
480 switch (type) {
481 case Resource::XSLStyleSheet:
482 ASSERT(RuntimeEnabledFeatures::xsltEnabled());
483 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
484 return false;
485 break;
486 case Resource::Script:
487 case Resource::ImportResource:
488 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowScriptFromSource(url))
489 return false;
490
491 if (frame()) {
492 Settings* settings = frame()->settings();
493 if (!frame()->loader().client()->allowScriptFromSource(!settings || settings->isScriptEnabled(), url)) {
494 frame()->loader().client()->didNotAllowScript();
495 return false;
496 }
497 }
498 break;
499 case Resource::Shader:
500 // Since shaders are referenced from CSS Styles use the same rules here.
501 case Resource::CSSStyleSheet:
502 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowStyleFromSource(url))
503 return false;
504 break;
505 case Resource::SVGDocument:
506 case Resource::Image:
507 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowImageFromSource(url))
508 return false;
509 break;
510 case Resource::Font: {
511 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowFontFromSource(url))
512 return false;
513 break;
514 }
515 case Resource::MainResource:
516 case Resource::Raw:
517 case Resource::LinkPrefetch:
518 case Resource::LinkSubresource:
519 break;
520 case Resource::TextTrack:
521 if (!shouldBypassMainWorldContentSecurityPolicy && !m_document->contentSecurityPolicy()->allowMediaFromSource(url))
522 return false;
523 break;
524 }
525
526 // Last of all, check for insecure content. We do this last so that when
527 // folks block insecure content with a CSP policy, they don't get a warning.
528 // They'll still get a warning in the console about CSP blocking the load.
529
530 // FIXME: Should we consider forPreload here?
531 if (!checkInsecureContent(type, url, options.mixedContentBlockingTreatment))
532 return false;
533
534 return true;
535 }
536
canAccess(Resource * resource,CORSEnabled corsEnabled,FetchRequest::OriginRestriction originRestriction)537 bool ResourceFetcher::canAccess(Resource* resource, CORSEnabled corsEnabled, FetchRequest::OriginRestriction originRestriction)
538 {
539 // Redirects can change the response URL different from one of request.
540 if (!canRequest(resource->type(), resource->response().url(), resource->options(), false, originRestriction))
541 return false;
542
543 String error;
544 switch (resource->type()) {
545 case Resource::Script:
546 case Resource::ImportResource:
547 if (corsEnabled == PotentiallyCORSEnabled
548 && !m_document->securityOrigin()->canRequest(resource->response().url())
549 && !resource->passesAccessControlCheck(m_document->securityOrigin(), error)) {
550 if (frame() && frame()->document())
551 frame()->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Script from origin '" + SecurityOrigin::create(resource->response().url())->toString() + "' has been blocked from loading by Cross-Origin Resource Sharing policy: " + error);
552 return false;
553 }
554
555 break;
556 default:
557 ASSERT_NOT_REACHED(); // FIXME: generalize to non-script resources
558 return false;
559 }
560
561 return true;
562 }
563
shouldLoadNewResource() const564 bool ResourceFetcher::shouldLoadNewResource() const
565 {
566 if (!frame())
567 return false;
568 if (!m_documentLoader)
569 return true;
570 if (m_documentLoader == frame()->loader().activeDocumentLoader())
571 return true;
572 return document() && document()->pageDismissalEventBeingDispatched() != Document::NoDismissal;
573 }
574
resourceNeedsLoad(Resource * resource,const FetchRequest & request,RevalidationPolicy policy)575 bool ResourceFetcher::resourceNeedsLoad(Resource* resource, const FetchRequest& request, RevalidationPolicy policy)
576 {
577 if (FetchRequest::DeferredByClient == request.defer())
578 return false;
579 if (policy != Use)
580 return true;
581 if (resource->stillNeedsLoad())
582 return true;
583 return request.options().synchronousPolicy == RequestSynchronously && resource->isLoading();
584 }
585
requestResource(Resource::Type type,FetchRequest & request)586 ResourcePtr<Resource> ResourceFetcher::requestResource(Resource::Type type, FetchRequest& request)
587 {
588 ASSERT(request.options().synchronousPolicy == RequestAsynchronously || type == Resource::Raw);
589
590 KURL url = request.resourceRequest().url();
591
592 WTF_LOG(ResourceLoading, "ResourceFetcher::requestResource '%s', charset '%s', priority=%d, forPreload=%u, type=%s", url.elidedString().latin1().data(), request.charset().latin1().data(), request.priority(), request.forPreload(), ResourceTypeName(type));
593
594 // If only the fragment identifiers differ, it is the same resource.
595 url = MemoryCache::removeFragmentIdentifierIfNeeded(url);
596
597 if (!url.isValid())
598 return 0;
599
600 if (!canRequest(type, url, request.options(), request.forPreload(), request.originRestriction()))
601 return 0;
602
603 if (Frame* f = frame())
604 f->loader().client()->dispatchWillRequestResource(&request);
605
606 // See if we can use an existing resource from the cache.
607 ResourcePtr<Resource> resource = memoryCache()->resourceForURL(url);
608
609 const RevalidationPolicy policy = determineRevalidationPolicy(type, request.mutableResourceRequest(), request.forPreload(), resource.get(), request.defer());
610 switch (policy) {
611 case Reload:
612 memoryCache()->remove(resource.get());
613 // Fall through
614 case Load:
615 resource = loadResource(type, request, request.charset());
616 break;
617 case Revalidate:
618 resource = revalidateResource(request, resource.get());
619 break;
620 case Use:
621 resource->updateForAccess();
622 notifyLoadedFromMemoryCache(resource.get());
623 break;
624 }
625
626 if (!resource)
627 return 0;
628
629 if (policy != Use)
630 resource->setIdentifier(createUniqueIdentifier());
631
632 if (!request.forPreload() || policy != Use) {
633 ResourceLoadPriority priority = loadPriority(type, request);
634 if (priority != resource->resourceRequest().priority()) {
635 resource->resourceRequest().setPriority(priority);
636 resource->didChangePriority(priority);
637 }
638 }
639
640 if (resourceNeedsLoad(resource.get(), request, policy)) {
641 if (!shouldLoadNewResource()) {
642 if (resource->inCache())
643 memoryCache()->remove(resource.get());
644 return 0;
645 }
646
647 if (!m_documentLoader || !m_documentLoader->scheduleArchiveLoad(resource.get(), request.resourceRequest()))
648 resource->load(this, request.options());
649
650 // For asynchronous loads that immediately fail, it's sufficient to return a
651 // null Resource, as it indicates that something prevented the load from starting.
652 // If there's a network error, that failure will happen asynchronously. However, if
653 // a sync load receives a network error, it will have already happened by this point.
654 // In that case, the requester should have access to the relevant ResourceError, so
655 // we need to return a non-null Resource.
656 if (resource->errorOccurred()) {
657 if (resource->inCache())
658 memoryCache()->remove(resource.get());
659 return request.options().synchronousPolicy == RequestSynchronously ? resource : 0;
660 }
661 }
662
663 // FIXME: Temporarily leave main resource caching disabled for chromium,
664 // see https://bugs.webkit.org/show_bug.cgi?id=107962. Before caching main
665 // resources, we should be sure to understand the implications for memory
666 // use.
667 //
668 // Ensure main resources aren't preloaded, and other main resource loads
669 // are removed from cache to prevent reuse.
670 if (type == Resource::MainResource) {
671 ASSERT(policy != Use);
672 ASSERT(policy != Revalidate);
673 memoryCache()->remove(resource.get());
674 if (request.forPreload())
675 return 0;
676 }
677
678 if (!request.resourceRequest().url().protocolIsData()) {
679 if (policy == Use && !m_validatedURLs.contains(request.resourceRequest().url())) {
680 // Resources loaded from memory cache should be reported the first time they're used.
681 RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime());
682 populateResourceTiming(info.get(), resource.get(), true);
683 m_scheduledResourceTimingReports.add(info, resource->type() == Resource::MainResource);
684 if (!m_resourceTimingReportTimer.isActive())
685 m_resourceTimingReportTimer.startOneShot(0);
686 }
687
688 m_validatedURLs.add(request.resourceRequest().url());
689 }
690
691 ASSERT(resource->url() == url.string());
692 m_documentResources.set(resource->url(), resource);
693 return resource;
694 }
695
resourceTimingReportTimerFired(Timer<ResourceFetcher> * timer)696 void ResourceFetcher::resourceTimingReportTimerFired(Timer<ResourceFetcher>* timer)
697 {
698 ASSERT_UNUSED(timer, timer == &m_resourceTimingReportTimer);
699 HashMap<RefPtr<ResourceTimingInfo>, bool> timingReports;
700 timingReports.swap(m_scheduledResourceTimingReports);
701 HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator end = timingReports.end();
702 for (HashMap<RefPtr<ResourceTimingInfo>, bool>::iterator it = timingReports.begin(); it != end; ++it) {
703 RefPtr<ResourceTimingInfo> info = it->key;
704 bool isMainResource = it->value;
705 reportResourceTiming(info.get(), document(), isMainResource);
706 }
707 }
708
determineTargetType(ResourceRequest & request,Resource::Type type)709 void ResourceFetcher::determineTargetType(ResourceRequest& request, Resource::Type type)
710 {
711 ResourceRequest::TargetType targetType = requestTargetType(this, request, type);
712 request.setTargetType(targetType);
713 }
714
resourceRequestCachePolicy(const ResourceRequest & request,Resource::Type type)715 ResourceRequestCachePolicy ResourceFetcher::resourceRequestCachePolicy(const ResourceRequest& request, Resource::Type type)
716 {
717 if (type == Resource::MainResource) {
718 FrameLoadType frameLoadType = frame()->loader().loadType();
719 bool isReload = frameLoadType == FrameLoadTypeReload || frameLoadType == FrameLoadTypeReloadFromOrigin;
720 if (request.httpMethod() == "POST" && frameLoadType == FrameLoadTypeBackForward)
721 return ReturnCacheDataDontLoad;
722 if (!m_documentLoader->overrideEncoding().isEmpty() || frameLoadType == FrameLoadTypeBackForward)
723 return ReturnCacheDataElseLoad;
724 if (isReload || frameLoadType == FrameLoadTypeSame || request.isConditional() || request.httpMethod() == "POST")
725 return ReloadIgnoringCacheData;
726 if (Frame* parent = frame()->tree().parent())
727 return parent->document()->fetcher()->resourceRequestCachePolicy(request, type);
728 return UseProtocolCachePolicy;
729 }
730
731 if (request.isConditional())
732 return ReloadIgnoringCacheData;
733
734 if (m_documentLoader && m_documentLoader->isLoadingInAPISense()) {
735 // For POST requests, we mutate the main resource's cache policy to avoid form resubmission.
736 // This policy should not be inherited by subresources.
737 ResourceRequestCachePolicy mainResourceCachePolicy = m_documentLoader->request().cachePolicy();
738 if (mainResourceCachePolicy == ReturnCacheDataDontLoad)
739 return ReturnCacheDataElseLoad;
740 return mainResourceCachePolicy;
741 }
742 return UseProtocolCachePolicy;
743 }
744
addAdditionalRequestHeaders(ResourceRequest & request,Resource::Type type)745 void ResourceFetcher::addAdditionalRequestHeaders(ResourceRequest& request, Resource::Type type)
746 {
747 if (!frame())
748 return;
749
750 if (request.cachePolicy() == UseProtocolCachePolicy)
751 request.setCachePolicy(resourceRequestCachePolicy(request, type));
752 if (request.targetType() == ResourceRequest::TargetIsUnspecified)
753 determineTargetType(request, type);
754 if (type == Resource::LinkPrefetch || type == Resource::LinkSubresource)
755 request.setHTTPHeaderField("Purpose", "prefetch");
756
757 context().addAdditionalRequestHeaders(*document(), request, type);
758 }
759
revalidateResource(const FetchRequest & request,Resource * resource)760 ResourcePtr<Resource> ResourceFetcher::revalidateResource(const FetchRequest& request, Resource* resource)
761 {
762 ASSERT(resource);
763 ASSERT(resource->inCache());
764 ASSERT(resource->isLoaded());
765 ASSERT(resource->canUseCacheValidator());
766 ASSERT(!resource->resourceToRevalidate());
767
768 ResourceRequest revalidatingRequest(resource->resourceRequest());
769 addAdditionalRequestHeaders(revalidatingRequest, resource->type());
770
771 const AtomicString& lastModified = resource->response().httpHeaderField("Last-Modified");
772 const AtomicString& eTag = resource->response().httpHeaderField("ETag");
773 if (!lastModified.isEmpty() || !eTag.isEmpty()) {
774 ASSERT(context().cachePolicy(document()) != CachePolicyReload);
775 if (context().cachePolicy(document()) == CachePolicyRevalidate)
776 revalidatingRequest.setHTTPHeaderField("Cache-Control", "max-age=0");
777 if (!lastModified.isEmpty())
778 revalidatingRequest.setHTTPHeaderField("If-Modified-Since", lastModified);
779 if (!eTag.isEmpty())
780 revalidatingRequest.setHTTPHeaderField("If-None-Match", eTag);
781 }
782
783 ResourcePtr<Resource> newResource = createResource(resource->type(), revalidatingRequest, resource->encoding());
784
785 WTF_LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.get(), resource);
786 newResource->setResourceToRevalidate(resource);
787
788 memoryCache()->remove(resource);
789 memoryCache()->add(newResource.get());
790 storeResourceTimingInitiatorInformation(newResource, request);
791 TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", newResource.get(), "url", newResource->url().string().ascii(), "priority", newResource->resourceRequest().priority());
792 return newResource;
793 }
794
loadResource(Resource::Type type,FetchRequest & request,const String & charset)795 ResourcePtr<Resource> ResourceFetcher::loadResource(Resource::Type type, FetchRequest& request, const String& charset)
796 {
797 ASSERT(!memoryCache()->resourceForURL(request.resourceRequest().url()));
798
799 WTF_LOG(ResourceLoading, "Loading Resource for '%s'.", request.resourceRequest().url().elidedString().latin1().data());
800
801 addAdditionalRequestHeaders(request.mutableResourceRequest(), type);
802 ResourcePtr<Resource> resource = createResource(type, request.mutableResourceRequest(), charset);
803
804 memoryCache()->add(resource.get());
805 storeResourceTimingInitiatorInformation(resource, request);
806 TRACE_EVENT_ASYNC_BEGIN2("net", "Resource", resource.get(), "url", resource->url().string().ascii(), "priority", resource->resourceRequest().priority());
807 return resource;
808 }
809
storeResourceTimingInitiatorInformation(const ResourcePtr<Resource> & resource,const FetchRequest & request)810 void ResourceFetcher::storeResourceTimingInitiatorInformation(const ResourcePtr<Resource>& resource, const FetchRequest& request)
811 {
812 if (request.options().requestInitiatorContext != DocumentContext)
813 return;
814
815 RefPtr<ResourceTimingInfo> info = ResourceTimingInfo::create(request.options().initiatorInfo.name, monotonicallyIncreasingTime());
816
817 if (resource->type() == Resource::MainResource) {
818 // <iframe>s should report the initial navigation requested by the parent document, but not subsequent navigations.
819 if (frame()->ownerElement() && !frame()->ownerElement()->loadedNonEmptyDocument()) {
820 info->setInitiatorType(frame()->ownerElement()->localName());
821 m_resourceTimingInfoMap.add(resource.get(), info);
822 frame()->ownerElement()->didLoadNonEmptyDocument();
823 }
824 } else {
825 m_resourceTimingInfoMap.add(resource.get(), info);
826 }
827 }
828
determineRevalidationPolicy(Resource::Type type,ResourceRequest & request,bool forPreload,Resource * existingResource,FetchRequest::DeferOption defer) const829 ResourceFetcher::RevalidationPolicy ResourceFetcher::determineRevalidationPolicy(Resource::Type type, ResourceRequest& request, bool forPreload, Resource* existingResource, FetchRequest::DeferOption defer) const
830 {
831 if (!existingResource)
832 return Load;
833
834 // We already have a preload going for this URL.
835 if (forPreload && existingResource->isPreloaded())
836 return Use;
837
838 // If the same URL has been loaded as a different type, we need to reload.
839 if (existingResource->type() != type) {
840 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to type mismatch.");
841 return Reload;
842 }
843
844 // Do not load from cache if images are not enabled. The load for this image will be blocked
845 // in ImageResource::load.
846 if (FetchRequest::DeferredByClient == defer)
847 return Reload;
848
849 // Always use data uris.
850 // FIXME: Extend this to non-images.
851 if (type == Resource::Image && request.url().protocolIsData())
852 return Use;
853
854 if (!existingResource->canReuse(request))
855 return Reload;
856
857 // Certain requests (e.g., XHRs) might have manually set headers that require revalidation.
858 // FIXME: In theory, this should be a Revalidate case. In practice, the MemoryCache revalidation path assumes a whole bunch
859 // of things about how revalidation works that manual headers violate, so punt to Reload instead.
860 if (request.isConditional())
861 return Reload;
862
863 // Don't reload resources while pasting.
864 if (m_allowStaleResources)
865 return Use;
866
867 // Alwaus use preloads.
868 if (existingResource->isPreloaded())
869 return Use;
870
871 // CachePolicyHistoryBuffer uses the cache no matter what.
872 CachePolicy cachePolicy = context().cachePolicy(document());
873 if (cachePolicy == CachePolicyHistoryBuffer)
874 return Use;
875
876 // Don't reuse resources with Cache-control: no-store.
877 if (existingResource->response().cacheControlContainsNoStore()) {
878 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to Cache-control: no-store.");
879 return Reload;
880 }
881
882 // If credentials were sent with the previous request and won't be
883 // with this one, or vice versa, re-fetch the resource.
884 //
885 // This helps with the case where the server sends back
886 // "Access-Control-Allow-Origin: *" all the time, but some of the
887 // client's requests are made without CORS and some with.
888 if (existingResource->resourceRequest().allowCookies() != request.allowCookies()) {
889 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to difference in credentials settings.");
890 return Reload;
891 }
892
893 // During the initial load, avoid loading the same resource multiple times for a single document,
894 // even if the cache policies would tell us to. Raw resources are exempted.
895 if (type != Resource::Raw && document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
896 return Use;
897
898 // CachePolicyReload always reloads
899 if (cachePolicy == CachePolicyReload) {
900 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to CachePolicyReload.");
901 return Reload;
902 }
903
904 // We'll try to reload the resource if it failed last time.
905 if (existingResource->errorOccurred()) {
906 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicye reloading due to resource being in the error state");
907 return Reload;
908 }
909
910 // For resources that are not yet loaded we ignore the cache policy.
911 if (existingResource->isLoading())
912 return Use;
913
914 // Check if the cache headers requires us to revalidate (cache expiration for example).
915 if (cachePolicy == CachePolicyRevalidate || existingResource->mustRevalidateDueToCacheHeaders()) {
916 // See if the resource has usable ETag or Last-modified headers.
917 if (existingResource->canUseCacheValidator())
918 return Revalidate;
919
920 // No, must reload.
921 WTF_LOG(ResourceLoading, "ResourceFetcher::determineRevalidationPolicy reloading due to missing cache validators.");
922 return Reload;
923 }
924
925 return Use;
926 }
927
printAccessDeniedMessage(const KURL & url) const928 void ResourceFetcher::printAccessDeniedMessage(const KURL& url) const
929 {
930 if (url.isNull())
931 return;
932
933 if (!frame())
934 return;
935
936 String message;
937 if (!m_document || m_document->url().isNull())
938 message = "Unsafe attempt to load URL " + url.elidedString() + '.';
939 else
940 message = "Unsafe attempt to load URL " + url.elidedString() + " from frame with URL " + m_document->url().elidedString() + ". Domains, protocols and ports must match.\n";
941
942 frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message);
943 }
944
setAutoLoadImages(bool enable)945 void ResourceFetcher::setAutoLoadImages(bool enable)
946 {
947 if (enable == m_autoLoadImages)
948 return;
949
950 m_autoLoadImages = enable;
951
952 if (!m_autoLoadImages)
953 return;
954
955 reloadImagesIfNotDeferred();
956 }
957
setImagesEnabled(bool enable)958 void ResourceFetcher::setImagesEnabled(bool enable)
959 {
960 if (enable == m_imagesEnabled)
961 return;
962
963 m_imagesEnabled = enable;
964
965 if (!m_imagesEnabled)
966 return;
967
968 reloadImagesIfNotDeferred();
969 }
970
clientDefersImage(const KURL & url) const971 bool ResourceFetcher::clientDefersImage(const KURL& url) const
972 {
973 return frame() && !frame()->loader().client()->allowImage(m_imagesEnabled, url);
974 }
975
shouldDeferImageLoad(const KURL & url) const976 bool ResourceFetcher::shouldDeferImageLoad(const KURL& url) const
977 {
978 return clientDefersImage(url) || !m_autoLoadImages;
979 }
980
reloadImagesIfNotDeferred()981 void ResourceFetcher::reloadImagesIfNotDeferred()
982 {
983 DocumentResourceMap::iterator end = m_documentResources.end();
984 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != end; ++it) {
985 Resource* resource = it->value.get();
986 if (resource->type() == Resource::Image && resource->stillNeedsLoad() && !clientDefersImage(resource->url()))
987 const_cast<Resource*>(resource)->load(this, defaultResourceOptions());
988 }
989 }
990
redirectReceived(Resource * resource,const ResourceResponse & redirectResponse)991 void ResourceFetcher::redirectReceived(Resource* resource, const ResourceResponse& redirectResponse)
992 {
993 ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
994 if (it != m_resourceTimingInfoMap.end())
995 it->value->addRedirect(redirectResponse);
996 }
997
didLoadResource(Resource * resource)998 void ResourceFetcher::didLoadResource(Resource* resource)
999 {
1000 RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader);
1001 RefPtr<Document> protectDocument(m_document);
1002
1003 if (resource && resource->response().isHTTP() && ((!resource->errorOccurred() && !resource->wasCanceled()) || resource->response().httpStatusCode() == 304) && document()) {
1004 ResourceTimingInfoMap::iterator it = m_resourceTimingInfoMap.find(resource);
1005 if (it != m_resourceTimingInfoMap.end()) {
1006 RefPtr<ResourceTimingInfo> info = it->value;
1007 m_resourceTimingInfoMap.remove(it);
1008 populateResourceTiming(info.get(), resource, false);
1009 reportResourceTiming(info.get(), document(), resource->type() == Resource::MainResource);
1010 }
1011 }
1012
1013 if (frame())
1014 frame()->loader().loadDone();
1015 performPostLoadActions();
1016
1017 if (!m_garbageCollectDocumentResourcesTimer.isActive())
1018 m_garbageCollectDocumentResourcesTimer.startOneShot(0);
1019 }
1020
1021 // Garbage collecting m_documentResources is a workaround for the
1022 // ResourcePtrs on the RHS being strong references. Ideally this
1023 // would be a weak map, however ResourcePtrs perform additional
1024 // bookkeeping on Resources, so instead pseudo-GC them -- when the
1025 // reference count reaches 1, m_documentResources is the only reference, so
1026 // remove it from the map.
garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher> * timer)1027 void ResourceFetcher::garbageCollectDocumentResourcesTimerFired(Timer<ResourceFetcher>* timer)
1028 {
1029 ASSERT_UNUSED(timer, timer == &m_garbageCollectDocumentResourcesTimer);
1030 garbageCollectDocumentResources();
1031 }
1032
garbageCollectDocumentResources()1033 void ResourceFetcher::garbageCollectDocumentResources()
1034 {
1035 typedef Vector<String, 10> StringVector;
1036 StringVector resourcesToDelete;
1037
1038 for (DocumentResourceMap::iterator it = m_documentResources.begin(); it != m_documentResources.end(); ++it) {
1039 if (it->value->hasOneHandle())
1040 resourcesToDelete.append(it->key);
1041 }
1042
1043 for (StringVector::const_iterator it = resourcesToDelete.begin(); it != resourcesToDelete.end(); ++it)
1044 m_documentResources.remove(*it);
1045 }
1046
performPostLoadActions()1047 void ResourceFetcher::performPostLoadActions()
1048 {
1049 checkForPendingPreloads();
1050 }
1051
notifyLoadedFromMemoryCache(Resource * resource)1052 void ResourceFetcher::notifyLoadedFromMemoryCache(Resource* resource)
1053 {
1054 if (!frame() || !frame()->page() || resource->status() != Resource::Cached || m_validatedURLs.contains(resource->url()))
1055 return;
1056 if (!resource->shouldSendResourceLoadCallbacks())
1057 return;
1058
1059 ResourceRequest request(resource->url());
1060 unsigned long identifier = createUniqueIdentifier();
1061 context().dispatchDidLoadResourceFromMemoryCache(request, resource->response());
1062 // FIXME: If willSendRequest changes the request, we don't respect it.
1063 willSendRequest(identifier, request, ResourceResponse(), resource->options());
1064 InspectorInstrumentation::markResourceAsCached(frame()->page(), identifier);
1065 context().sendRemainingDelegateMessages(m_documentLoader, identifier, resource->response(), resource->encodedSize());
1066 }
1067
incrementRequestCount(const Resource * res)1068 void ResourceFetcher::incrementRequestCount(const Resource* res)
1069 {
1070 if (res->ignoreForRequestCount())
1071 return;
1072
1073 ++m_requestCount;
1074 }
1075
decrementRequestCount(const Resource * res)1076 void ResourceFetcher::decrementRequestCount(const Resource* res)
1077 {
1078 if (res->ignoreForRequestCount())
1079 return;
1080
1081 --m_requestCount;
1082 ASSERT(m_requestCount > -1);
1083 }
1084
preload(Resource::Type type,FetchRequest & request,const String & charset)1085 void ResourceFetcher::preload(Resource::Type type, FetchRequest& request, const String& charset)
1086 {
1087 requestPreload(type, request, charset);
1088 }
1089
checkForPendingPreloads()1090 void ResourceFetcher::checkForPendingPreloads()
1091 {
1092 // FIXME: It seems wrong to poke body()->renderer() here.
1093 if (m_pendingPreloads.isEmpty() || !m_document->body() || !m_document->body()->renderer())
1094 return;
1095 while (!m_pendingPreloads.isEmpty()) {
1096 PendingPreload preload = m_pendingPreloads.takeFirst();
1097 // 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).
1098 if (!cachedResource(preload.m_request.resourceRequest().url()))
1099 requestPreload(preload.m_type, preload.m_request, preload.m_charset);
1100 }
1101 m_pendingPreloads.clear();
1102 }
1103
requestPreload(Resource::Type type,FetchRequest & request,const String & charset)1104 void ResourceFetcher::requestPreload(Resource::Type type, FetchRequest& request, const String& charset)
1105 {
1106 String encoding;
1107 if (type == Resource::Script || type == Resource::CSSStyleSheet)
1108 encoding = charset.isEmpty() ? m_document->charset().string() : charset;
1109
1110 request.setCharset(encoding);
1111 request.setForPreload(true);
1112
1113 ResourcePtr<Resource> resource = requestResource(type, request);
1114 if (!resource || (m_preloads && m_preloads->contains(resource.get())))
1115 return;
1116 TRACE_EVENT_ASYNC_STEP_INTO0("net", "Resource", resource.get(), "Preload");
1117 resource->increasePreloadCount();
1118
1119 if (!m_preloads)
1120 m_preloads = adoptPtr(new ListHashSet<Resource*>);
1121 m_preloads->add(resource.get());
1122
1123 #if PRELOAD_DEBUG
1124 printf("PRELOADING %s\n", resource->url().latin1().data());
1125 #endif
1126 }
1127
isPreloaded(const String & urlString) const1128 bool ResourceFetcher::isPreloaded(const String& urlString) const
1129 {
1130 const KURL& url = m_document->completeURL(urlString);
1131
1132 if (m_preloads) {
1133 ListHashSet<Resource*>::iterator end = m_preloads->end();
1134 for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) {
1135 Resource* resource = *it;
1136 if (resource->url() == url)
1137 return true;
1138 }
1139 }
1140
1141 Deque<PendingPreload>::const_iterator dequeEnd = m_pendingPreloads.end();
1142 for (Deque<PendingPreload>::const_iterator it = m_pendingPreloads.begin(); it != dequeEnd; ++it) {
1143 PendingPreload pendingPreload = *it;
1144 if (pendingPreload.m_request.resourceRequest().url() == url)
1145 return true;
1146 }
1147 return false;
1148 }
1149
clearPreloads()1150 void ResourceFetcher::clearPreloads()
1151 {
1152 #if PRELOAD_DEBUG
1153 printPreloadStats();
1154 #endif
1155 if (!m_preloads)
1156 return;
1157
1158 ListHashSet<Resource*>::iterator end = m_preloads->end();
1159 for (ListHashSet<Resource*>::iterator it = m_preloads->begin(); it != end; ++it) {
1160 Resource* res = *it;
1161 res->decreasePreloadCount();
1162 bool deleted = res->deleteIfPossible();
1163 if (!deleted && res->preloadResult() == Resource::PreloadNotReferenced)
1164 memoryCache()->remove(res);
1165 }
1166 m_preloads.clear();
1167 }
1168
clearPendingPreloads()1169 void ResourceFetcher::clearPendingPreloads()
1170 {
1171 m_pendingPreloads.clear();
1172 }
1173
didFinishLoading(const Resource * resource,double finishTime,const ResourceLoaderOptions & options)1174 void ResourceFetcher::didFinishLoading(const Resource* resource, double finishTime, const ResourceLoaderOptions& options)
1175 {
1176 TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
1177 if (options.sendLoadCallbacks != SendCallbacks)
1178 return;
1179 context().dispatchDidFinishLoading(m_documentLoader, resource->identifier(), finishTime);
1180 }
1181
didChangeLoadingPriority(const Resource * resource,ResourceLoadPriority loadPriority)1182 void ResourceFetcher::didChangeLoadingPriority(const Resource* resource, ResourceLoadPriority loadPriority)
1183 {
1184 TRACE_EVENT_ASYNC_STEP_INTO1("net", "Resource", resource, "ChangePriority", "priority", loadPriority);
1185 context().dispatchDidChangeResourcePriority(resource->identifier(), loadPriority);
1186 }
1187
didFailLoading(const Resource * resource,const ResourceError & error,const ResourceLoaderOptions & options)1188 void ResourceFetcher::didFailLoading(const Resource* resource, const ResourceError& error, const ResourceLoaderOptions& options)
1189 {
1190 TRACE_EVENT_ASYNC_END0("net", "Resource", resource);
1191 if (options.sendLoadCallbacks != SendCallbacks)
1192 return;
1193 context().dispatchDidFail(m_documentLoader, resource->identifier(), error);
1194 }
1195
willSendRequest(unsigned long identifier,ResourceRequest & request,const ResourceResponse & redirectResponse,const ResourceLoaderOptions & options)1196 void ResourceFetcher::willSendRequest(unsigned long identifier, ResourceRequest& request, const ResourceResponse& redirectResponse, const ResourceLoaderOptions& options)
1197 {
1198 if (options.sendLoadCallbacks == SendCallbacks)
1199 context().dispatchWillSendRequest(m_documentLoader, identifier, request, redirectResponse, options.initiatorInfo);
1200 else
1201 InspectorInstrumentation::willSendRequest(frame(), identifier, m_documentLoader, request, redirectResponse, options.initiatorInfo);
1202 }
1203
didReceiveResponse(const Resource * resource,const ResourceResponse & response,const ResourceLoaderOptions & options)1204 void ResourceFetcher::didReceiveResponse(const Resource* resource, const ResourceResponse& response, const ResourceLoaderOptions& options)
1205 {
1206 if (options.sendLoadCallbacks != SendCallbacks)
1207 return;
1208 context().dispatchDidReceiveResponse(m_documentLoader, resource->identifier(), response, resource->loader());
1209 }
1210
didReceiveData(const Resource * resource,const char * data,int dataLength,int encodedDataLength,const ResourceLoaderOptions & options)1211 void ResourceFetcher::didReceiveData(const Resource* resource, const char* data, int dataLength, int encodedDataLength, const ResourceLoaderOptions& options)
1212 {
1213 // FIXME: use frame of master document for imported documents.
1214 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(frame(), resource->identifier(), encodedDataLength);
1215 if (options.sendLoadCallbacks != SendCallbacks)
1216 return;
1217 context().dispatchDidReceiveData(m_documentLoader, resource->identifier(), data, dataLength, encodedDataLength);
1218 InspectorInstrumentation::didReceiveResourceData(cookie);
1219 }
1220
didDownloadData(const Resource * resource,int dataLength,int encodedDataLength,const ResourceLoaderOptions & options)1221 void ResourceFetcher::didDownloadData(const Resource* resource, int dataLength, int encodedDataLength, const ResourceLoaderOptions& options)
1222 {
1223 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willReceiveResourceData(frame(), resource->identifier(), encodedDataLength);
1224 if (options.sendLoadCallbacks != SendCallbacks)
1225 return;
1226 context().dispatchDidDownloadData(m_documentLoader, resource->identifier(), dataLength, encodedDataLength);
1227 InspectorInstrumentation::didReceiveResourceData(cookie);
1228 }
1229
subresourceLoaderFinishedLoadingOnePart(ResourceLoader * loader)1230 void ResourceFetcher::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader)
1231 {
1232 if (m_multipartLoaders)
1233 m_multipartLoaders->add(loader);
1234 if (m_loaders)
1235 m_loaders->remove(loader);
1236 if (Frame* frame = this->frame())
1237 return frame->loader().checkLoadComplete(m_documentLoader);
1238 }
1239
didInitializeResourceLoader(ResourceLoader * loader)1240 void ResourceFetcher::didInitializeResourceLoader(ResourceLoader* loader)
1241 {
1242 if (!m_document)
1243 return;
1244 if (!m_loaders)
1245 m_loaders = adoptPtr(new ResourceLoaderSet());
1246 ASSERT(!m_loaders->contains(loader));
1247 m_loaders->add(loader);
1248 }
1249
willTerminateResourceLoader(ResourceLoader * loader)1250 void ResourceFetcher::willTerminateResourceLoader(ResourceLoader* loader)
1251 {
1252 if (!m_loaders || !m_loaders->contains(loader))
1253 return;
1254 m_loaders->remove(loader);
1255 if (Frame* frame = this->frame())
1256 frame->loader().checkLoadComplete(m_documentLoader);
1257 }
1258
willStartLoadingResource(ResourceRequest & request)1259 void ResourceFetcher::willStartLoadingResource(ResourceRequest& request)
1260 {
1261 if (m_documentLoader)
1262 m_documentLoader->applicationCacheHost()->willStartLoadingResource(request);
1263 }
1264
stopFetching()1265 void ResourceFetcher::stopFetching()
1266 {
1267 if (m_multipartLoaders)
1268 m_multipartLoaders->cancelAll();
1269 if (m_loaders)
1270 m_loaders->cancelAll();
1271 }
1272
isFetching() const1273 bool ResourceFetcher::isFetching() const
1274 {
1275 return m_loaders && !m_loaders->isEmpty();
1276 }
1277
setDefersLoading(bool defers)1278 void ResourceFetcher::setDefersLoading(bool defers)
1279 {
1280 if (m_loaders)
1281 m_loaders->setAllDefersLoading(defers);
1282 }
1283
defersLoading() const1284 bool ResourceFetcher::defersLoading() const
1285 {
1286 if (Frame* frame = this->frame())
1287 return frame->page()->defersLoading();
1288 return false;
1289 }
1290
isLoadedBy(ResourceLoaderHost * possibleOwner) const1291 bool ResourceFetcher::isLoadedBy(ResourceLoaderHost* possibleOwner) const
1292 {
1293 return this == possibleOwner;
1294 }
1295
shouldRequest(Resource * resource,const ResourceRequest & request,const ResourceLoaderOptions & options)1296 bool ResourceFetcher::shouldRequest(Resource* resource, const ResourceRequest& request, const ResourceLoaderOptions& options)
1297 {
1298 if (!canRequest(resource->type(), request.url(), options, false, FetchRequest::UseDefaultOriginRestrictionForType))
1299 return false;
1300 if (resource->type() == Resource::Image && shouldDeferImageLoad(request.url()))
1301 return false;
1302 return true;
1303 }
1304
refResourceLoaderHost()1305 void ResourceFetcher::refResourceLoaderHost()
1306 {
1307 ref();
1308 }
1309
derefResourceLoaderHost()1310 void ResourceFetcher::derefResourceLoaderHost()
1311 {
1312 deref();
1313 }
1314
1315 #if PRELOAD_DEBUG
printPreloadStats()1316 void ResourceFetcher::printPreloadStats()
1317 {
1318 unsigned scripts = 0;
1319 unsigned scriptMisses = 0;
1320 unsigned stylesheets = 0;
1321 unsigned stylesheetMisses = 0;
1322 unsigned images = 0;
1323 unsigned imageMisses = 0;
1324 ListHashSet<Resource*>::iterator end = m_preloads.end();
1325 for (ListHashSet<Resource*>::iterator it = m_preloads.begin(); it != end; ++it) {
1326 Resource* res = *it;
1327 if (res->preloadResult() == Resource::PreloadNotReferenced)
1328 printf("!! UNREFERENCED PRELOAD %s\n", res->url().latin1().data());
1329 else if (res->preloadResult() == Resource::PreloadReferencedWhileComplete)
1330 printf("HIT COMPLETE PRELOAD %s\n", res->url().latin1().data());
1331 else if (res->preloadResult() == Resource::PreloadReferencedWhileLoading)
1332 printf("HIT LOADING PRELOAD %s\n", res->url().latin1().data());
1333
1334 if (res->type() == Resource::Script) {
1335 scripts++;
1336 if (res->preloadResult() < Resource::PreloadReferencedWhileLoading)
1337 scriptMisses++;
1338 } else if (res->type() == Resource::CSSStyleSheet) {
1339 stylesheets++;
1340 if (res->preloadResult() < Resource::PreloadReferencedWhileLoading)
1341 stylesheetMisses++;
1342 } else {
1343 images++;
1344 if (res->preloadResult() < Resource::PreloadReferencedWhileLoading)
1345 imageMisses++;
1346 }
1347
1348 if (res->errorOccurred())
1349 memoryCache()->remove(res);
1350
1351 res->decreasePreloadCount();
1352 }
1353 m_preloads.clear();
1354
1355 if (scripts)
1356 printf("SCRIPTS: %d (%d hits, hit rate %d%%)\n", scripts, scripts - scriptMisses, (scripts - scriptMisses) * 100 / scripts);
1357 if (stylesheets)
1358 printf("STYLESHEETS: %d (%d hits, hit rate %d%%)\n", stylesheets, stylesheets - stylesheetMisses, (stylesheets - stylesheetMisses) * 100 / stylesheets);
1359 if (images)
1360 printf("IMAGES: %d (%d hits, hit rate %d%%)\n", images, images - imageMisses, (images - imageMisses) * 100 / images);
1361 }
1362 #endif
1363
defaultResourceOptions()1364 const ResourceLoaderOptions& ResourceFetcher::defaultResourceOptions()
1365 {
1366 DEFINE_STATIC_LOCAL(ResourceLoaderOptions, options, (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, DoSecurityCheck, CheckContentSecurityPolicy, DocumentContext));
1367 return options;
1368 }
1369
1370 }
1371