38719bbdc4d19ce163f8b32fcd7376e24ebfce51
[WebKit-https.git] / Source / WebCore / loader / MainResourceLoader.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "MainResourceLoader.h"
32
33 #include "ApplicationCacheHost.h"
34 #include "BackForwardController.h"
35 #include "CachedRawResource.h"
36 #include "CachedResourceLoader.h"
37 #include "CachedResourceRequest.h"
38 #include "Console.h"
39 #include "DOMWindow.h"
40 #include "Document.h"
41 #include "DocumentLoadTiming.h"
42 #include "DocumentLoader.h"
43 #include "FeatureObserver.h"
44 #include "FormState.h"
45 #include "Frame.h"
46 #include "FrameLoader.h"
47 #include "FrameLoaderClient.h"
48 #include "HTMLFormElement.h"
49 #include "HistoryItem.h"
50 #include "InspectorInstrumentation.h"
51 #include "MemoryCache.h"
52 #include "Page.h"
53 #include "ProgressTracker.h"
54 #include "ResourceBuffer.h"
55 #include "ResourceError.h"
56 #include "ResourceHandle.h"
57 #include "SchemeRegistry.h"
58 #include "SecurityOrigin.h"
59 #include "Settings.h"
60 #include "SubresourceLoader.h"
61 #include <wtf/CurrentTime.h>
62
63 #if PLATFORM(QT)
64 #include "PluginDatabase.h"
65 #endif
66
67 #if USE(CONTENT_FILTERING)
68 #include "ContentFilter.h"
69 #endif
70
71 namespace WebCore {
72
73 MainResourceLoader::MainResourceLoader(DocumentLoader* documentLoader)
74     : m_dataLoadTimer(this, &MainResourceLoader::handleSubstituteDataLoadNow)
75     , m_documentLoader(documentLoader)
76     , m_loadingMultipartContent(false)
77     , m_waitingForContentPolicy(false)
78     , m_timeOfLastDataReceived(0.0)
79     , m_identifierForLoadWithoutResourceLoader(0)
80 {
81 }
82
83 MainResourceLoader::~MainResourceLoader()
84 {
85     clearResource();
86 }
87
88 PassRefPtr<MainResourceLoader> MainResourceLoader::create(DocumentLoader* documentLoader)
89 {
90     return adoptRef(new MainResourceLoader(documentLoader));
91 }
92
93 void MainResourceLoader::receivedError(const ResourceError& error)
94 {
95     // Calling receivedMainResourceError will likely result in the last reference to this object to go away.
96     RefPtr<MainResourceLoader> protect(this);
97     RefPtr<Frame> protectFrame(m_documentLoader->frame());
98
99     if (m_identifierForLoadWithoutResourceLoader) {
100         ASSERT(!loader());
101         frameLoader()->client()->dispatchDidFailLoading(documentLoader(), m_identifierForLoadWithoutResourceLoader, error);
102     }
103
104     // It is important that we call DocumentLoader::mainReceivedError before calling 
105     // ResourceLoadNotifier::didFailToLoad because mainReceivedError clears out the relevant
106     // document loaders. Also, mainReceivedError ends up calling a FrameLoadDelegate method
107     // and didFailToLoad calls a ResourceLoadDelegate method and they need to be in the correct order.
108     documentLoader()->mainReceivedError(error);
109 }
110
111 void MainResourceLoader::cancel()
112 {
113     cancel(ResourceError());
114 }
115
116 void MainResourceLoader::cancel(const ResourceError& error)
117 {
118     RefPtr<MainResourceLoader> protect(this);
119     ResourceError resourceError = error.isNull() ? frameLoader()->cancelledError(request()) : error;
120
121     m_dataLoadTimer.stop();
122
123     if (m_waitingForContentPolicy) {
124         frameLoader()->policyChecker()->cancelCheck();
125         ASSERT(m_waitingForContentPolicy);
126         m_waitingForContentPolicy = false;
127         deref(); // balances ref in responseReceived
128     }
129
130     if (loader())
131         loader()->cancel(resourceError);
132
133     clearResource();
134     receivedError(resourceError);
135 }
136
137 void MainResourceLoader::clearResource()
138 {
139     if (m_resource) {
140         m_resource->removeClient(this);
141         m_resource = 0;
142     }
143 }
144
145 FrameLoader* MainResourceLoader::frameLoader() const
146 {
147     return m_documentLoader->frameLoader();
148 }
149
150 const ResourceRequest& MainResourceLoader::request() const
151 {
152     return m_resource ? m_resource->resourceRequest() : m_initialRequest;
153 }
154
155 ResourceError MainResourceLoader::interruptedForPolicyChangeError() const
156 {
157     return frameLoader()->client()->interruptedForPolicyChangeError(request());
158 }
159
160 void MainResourceLoader::stopLoadingForPolicyChange()
161 {
162     ResourceError error = interruptedForPolicyChangeError();
163     error.setIsCancellation(true);
164     cancel(error);
165 }
166
167 void MainResourceLoader::callContinueAfterNavigationPolicy(void* argument, const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue)
168 {
169     static_cast<MainResourceLoader*>(argument)->continueAfterNavigationPolicy(request, shouldContinue);
170 }
171
172 void MainResourceLoader::continueAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue)
173 {
174     if (!shouldContinue)
175         stopLoadingForPolicyChange();
176     else if (m_substituteData.isValid()) {
177         // A redirect resulted in loading substitute data.
178         ASSERT(documentLoader()->timing()->redirectCount());
179
180         // We need to remove our reference to the CachedResource in favor of a SubstituteData load.
181         // This will probably trigger the cancellation of the CachedResource's underlying ResourceLoader, though there is a
182         // small chance that the resource is being loaded by a different Frame, preventing the ResourceLoader from being cancelled.
183         // If the ResourceLoader is indeed cancelled, it would normally send resource load callbacks.
184         // However, from an API perspective, this isn't a cancellation. Therefore, sever our relationship with the network load via clearResource(),
185         // but prevent the ResourceLoader from sending ResourceLoadNotifier callbacks.
186         RefPtr<ResourceLoader> resourceLoader = loader();
187         ASSERT(resourceLoader->shouldSendResourceLoadCallbacks());
188         resourceLoader->setSendCallbackPolicy(DoNotSendCallbacks);
189         clearResource();
190         resourceLoader->setSendCallbackPolicy(SendCallbacks);
191         handleSubstituteDataLoadSoon(request);
192     }
193
194     deref(); // balances ref in willSendRequest
195 }
196
197 bool MainResourceLoader::isPostOrRedirectAfterPost(const ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
198 {
199     if (newRequest.httpMethod() == "POST")
200         return true;
201
202     int status = redirectResponse.httpStatusCode();
203     if (((status >= 301 && status <= 303) || status == 307)
204         && frameLoader()->initialRequest().httpMethod() == "POST")
205         return true;
206     
207     return false;
208 }
209
210 PassRefPtr<ResourceBuffer> MainResourceLoader::resourceData()
211 {
212     ASSERT(!m_resource || !m_substituteData.isValid());
213     if (m_resource)
214         return m_resource->resourceBuffer();
215     if (m_substituteData.isValid())
216         return ResourceBuffer::create(m_substituteData.content()->data(), m_substituteData.content()->size());
217     return 0;
218 }
219
220 void MainResourceLoader::redirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
221 {
222     ASSERT_UNUSED(resource, resource == m_resource);
223     willSendRequest(request, redirectResponse);
224 }
225
226 void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
227 {
228     // Note that there are no asserts here as there are for the other callbacks. This is due to the
229     // fact that this "callback" is sent when starting every load, and the state of callback
230     // deferrals plays less of a part in this function in preventing the bad behavior deferring 
231     // callbacks is meant to prevent.
232     ASSERT(!newRequest.isNull());
233
234     // The additional processing can do anything including possibly removing the last
235     // reference to this object; one example of this is 3266216.
236     RefPtr<MainResourceLoader> protect(this);
237
238     if (!frameLoader()->checkIfFormActionAllowedByCSP(newRequest.url())) {
239         cancel();
240         return;
241     }
242
243     ASSERT(documentLoader()->timing()->fetchStart());
244     if (!redirectResponse.isNull()) {
245         // If the redirecting url is not allowed to display content from the target origin,
246         // then block the redirect.
247         RefPtr<SecurityOrigin> redirectingOrigin = SecurityOrigin::create(redirectResponse.url());
248         if (!redirectingOrigin->canDisplay(newRequest.url())) {
249             FrameLoader::reportLocalLoadFailed(m_documentLoader->frame(), newRequest.url().string());
250             cancel();
251             return;
252         }
253         documentLoader()->timing()->addRedirect(redirectResponse.url(), newRequest.url());
254     }
255
256     // Update cookie policy base URL as URL changes, except for subframes, which use the
257     // URL of the main frame which doesn't change when we redirect.
258     if (frameLoader()->isLoadingMainFrame())
259         newRequest.setFirstPartyForCookies(newRequest.url());
260
261     // If we're fielding a redirect in response to a POST, force a load from origin, since
262     // this is a common site technique to return to a page viewing some data that the POST
263     // just modified.
264     // Also, POST requests always load from origin, but this does not affect subresources.
265     if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse))
266         newRequest.setCachePolicy(ReloadIgnoringCacheData);
267
268     Frame* top = m_documentLoader->frame()->tree()->top();
269     if (top != m_documentLoader->frame()) {
270         if (!frameLoader()->mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), newRequest.url())) {
271             cancel();
272             return;
273         }
274     }
275
276     // Don't set this on the first request. It is set when the main load was started.
277     m_documentLoader->setRequest(newRequest);
278
279     if (!redirectResponse.isNull()) {
280         // We checked application cache for initial URL, now we need to check it for redirected one.
281         ASSERT(!m_substituteData.isValid());
282         documentLoader()->applicationCacheHost()->maybeLoadMainResourceForRedirect(newRequest, m_substituteData);
283         if (m_substituteData.isValid())
284             m_identifierForLoadWithoutResourceLoader = identifier();
285     }
286
287     // FIXME: Ideally we'd stop the I/O until we hear back from the navigation policy delegate
288     // listener. But there's no way to do that in practice. So instead we cancel later if the
289     // listener tells us to. In practice that means the navigation policy needs to be decided
290     // synchronously for these redirect cases.
291     if (!redirectResponse.isNull()) {
292         ref(); // balanced by deref in continueAfterNavigationPolicy
293         frameLoader()->policyChecker()->checkNavigationPolicy(newRequest, callContinueAfterNavigationPolicy, this);
294     }
295 }
296
297 void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, const ResourceResponse& r)
298 {
299     KURL url = request().url();
300     const String& mimeType = r.mimeType();
301     
302     switch (contentPolicy) {
303     case PolicyUse: {
304         // Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255).
305         bool isRemoteWebArchive = (equalIgnoringCase("application/x-webarchive", mimeType)
306 #if PLATFORM(GTK)
307                                    || equalIgnoringCase("message/rfc822", mimeType)
308 #endif
309                                    || equalIgnoringCase("multipart/related", mimeType))
310             && !m_substituteData.isValid() && !SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol());
311         if (!frameLoader()->client()->canShowMIMEType(mimeType) || isRemoteWebArchive) {
312             frameLoader()->policyChecker()->cannotShowMIMEType(r);
313             // Check reachedTerminalState since the load may have already been canceled inside of _handleUnimplementablePolicyWithErrorCode::.
314             stopLoadingForPolicyChange();
315             return;
316         }
317         break;
318     }
319
320     case PolicyDownload: {
321         // m_resource can be null, e.g. when loading a substitute resource from application cache.
322         if (!m_resource) {
323             receivedError(frameLoader()->client()->cannotShowURLError(request()));
324             return;
325         }
326         InspectorInstrumentation::continueWithPolicyDownload(m_documentLoader->frame(), documentLoader(), identifier(), r);
327
328         // When starting the request, we didn't know that it would result in download and not navigation. Now we know that main document URL didn't change.
329         // Download may use this knowledge for purposes unrelated to cookies, notably for setting file quarantine data.
330         ResourceRequest request = this->request();
331         frameLoader()->setOriginalURLForDownloadRequest(request);
332
333         frameLoader()->client()->convertMainResourceLoadToDownload(this, request, r);
334
335         // It might have gone missing
336         if (loader())
337             loader()->didFail(interruptedForPolicyChangeError());
338         return;
339     }
340     case PolicyIgnore:
341         InspectorInstrumentation::continueWithPolicyIgnore(m_documentLoader->frame(), documentLoader(), identifier(), r);
342         stopLoadingForPolicyChange();
343         return;
344     
345     default:
346         ASSERT_NOT_REACHED();
347     }
348
349     RefPtr<MainResourceLoader> protect(this);
350
351     if (r.isHTTP()) {
352         int status = r.httpStatusCode();
353         if (status < 200 || status >= 300) {
354             bool hostedByObject = frameLoader()->isHostedByObjectElement();
355
356             frameLoader()->handleFallbackContent();
357             // object elements are no longer rendered after we fallback, so don't
358             // keep trying to process data from their load
359
360             if (hostedByObject)
361                 cancel();
362         }
363     }
364
365     if (!m_documentLoader->isStopping() && m_substituteData.isValid()) {
366         if (m_substituteData.content()->size())
367             dataReceived(0, m_substituteData.content()->data(), m_substituteData.content()->size());
368         if (m_documentLoader->isLoadingMainResource())
369             didFinishLoading(0);
370     }
371 }
372
373 void MainResourceLoader::callContinueAfterContentPolicy(void* argument, PolicyAction policy)
374 {
375     static_cast<MainResourceLoader*>(argument)->continueAfterContentPolicy(policy);
376 }
377
378 void MainResourceLoader::continueAfterContentPolicy(PolicyAction policy)
379 {
380     ASSERT(m_waitingForContentPolicy);
381     m_waitingForContentPolicy = false;
382     if (!m_documentLoader->isStopping())
383         continueAfterContentPolicy(policy, m_response);
384     deref(); // balances ref in responseReceived
385 }
386
387 void MainResourceLoader::responseReceived(CachedResource* resource, const ResourceResponse& r)
388 {
389     ASSERT_UNUSED(resource, m_resource == resource);
390     bool willLoadFallback = documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainResponse(request(), r);
391
392     // The memory cache doesn't understand the application cache or its caching rules. So if a main resource is served
393     // from the application cache, ensure we don't save the result for future use.
394     bool shouldRemoveResourceFromCache = willLoadFallback;
395 #if PLATFORM(CHROMIUM)
396     // chromium's ApplicationCacheHost implementation always returns true for maybeLoadFallbackForMainResponse(). However, all responses loaded
397     // from appcache will have a non-zero appCacheID().
398     if (r.appCacheID())
399         shouldRemoveResourceFromCache = true;
400 #endif
401     if (shouldRemoveResourceFromCache)
402         memoryCache()->remove(m_resource.get());
403
404     if (willLoadFallback)
405         return;
406
407     DEFINE_STATIC_LOCAL(AtomicString, xFrameOptionHeader, ("x-frame-options", AtomicString::ConstructFromLiteral));
408     HTTPHeaderMap::const_iterator it = r.httpHeaderFields().find(xFrameOptionHeader);
409     if (it != r.httpHeaderFields().end()) {
410         String content = it->value;
411         if (frameLoader()->shouldInterruptLoadForXFrameOptions(content, r.url(), identifier())) {
412             InspectorInstrumentation::continueAfterXFrameOptionsDenied(m_documentLoader->frame(), documentLoader(), identifier(), r);
413             String message = "Refused to display '" + r.url().string() + "' in a frame because it set 'X-Frame-Options' to '" + content + "'.";
414             m_documentLoader->frame()->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, message, identifier());
415
416             cancel();
417             return;
418         }
419     }
420
421     // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
422     // See <rdar://problem/6304600> for more details.
423 #if !USE(CF)
424     ASSERT(!defersLoading());
425 #endif
426
427     if (m_loadingMultipartContent) {
428         m_documentLoader->setupForReplace();
429         m_resource->clear();
430     }
431     
432     if (r.isMultipart()) {
433         FeatureObserver::observe(m_documentLoader->frame()->document(), FeatureObserver::MultipartMainResource);
434         m_loadingMultipartContent = true;
435     }
436         
437     // The additional processing can do anything including possibly removing the last
438     // reference to this object; one example of this is 3266216.
439     RefPtr<MainResourceLoader> protect(this);
440
441     m_documentLoader->setResponse(r);
442
443     m_response = r;
444
445     if (m_identifierForLoadWithoutResourceLoader)
446         frameLoader()->notifier()->dispatchDidReceiveResponse(documentLoader(), identifier(), m_response, 0);
447
448     ASSERT(!m_waitingForContentPolicy);
449     m_waitingForContentPolicy = true;
450     ref(); // balanced by deref in continueAfterContentPolicy and cancel
451
452     // Always show content with valid substitute data.
453     if (m_documentLoader->substituteData().isValid()) {
454         callContinueAfterContentPolicy(this, PolicyUse);
455         return;
456     }
457
458 #if ENABLE(FTPDIR)
459     // Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it
460     Settings* settings = m_documentLoader->frame()->settings();
461     if (settings && settings->forceFTPDirectoryListings() && m_response.mimeType() == "application/x-ftp-directory") {
462         callContinueAfterContentPolicy(this, PolicyUse);
463         return;
464     }
465 #endif
466
467 #if USE(CONTENT_FILTERING)
468     if (r.url().protocolIs("https") && ContentFilter::isEnabled())
469         m_contentFilter = ContentFilter::create(r);
470 #endif
471
472     frameLoader()->policyChecker()->checkContentPolicy(m_response, callContinueAfterContentPolicy, this);
473 }
474
475 void MainResourceLoader::dataReceived(CachedResource* resource, const char* data, int length)
476 {
477     ASSERT(data);
478     ASSERT(length != 0);
479     ASSERT_UNUSED(resource, resource == m_resource);
480     ASSERT(!m_response.isNull());
481
482 #if USE(CFNETWORK) || PLATFORM(MAC)
483     // Workaround for <rdar://problem/6060782>
484     if (m_response.isNull()) {
485         m_response = ResourceResponse(KURL(), "text/html", 0, String(), String());
486         if (DocumentLoader* documentLoader = m_documentLoader.get())
487             documentLoader->setResponse(m_response);
488     }
489 #endif
490
491     // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
492     // See <rdar://problem/6304600> for more details.
493 #if !USE(CF)
494     ASSERT(!defersLoading());
495 #endif
496
497 #if USE(CONTENT_FILTERING)
498     bool loadWasBlockedBeforeFinishing = false;
499     if (m_contentFilter && m_contentFilter->needsMoreData()) {
500         m_contentFilter->addData(data, length);
501
502         if (m_contentFilter->needsMoreData()) {
503             // Since the filter still needs more data to make a decision,
504             // transition back to the committed state so that we don't partially
505             // load content that might later be blocked.
506             documentLoader()->receivedData(0, 0);
507             return;
508         }
509
510         data = m_contentFilter->getReplacementData(length);
511         loadWasBlockedBeforeFinishing = m_contentFilter->didBlockData();
512     }
513 #endif
514
515     if (m_identifierForLoadWithoutResourceLoader)
516         frameLoader()->notifier()->dispatchDidReceiveData(documentLoader(), identifier(), data, length, -1);
517
518     documentLoader()->applicationCacheHost()->mainResourceDataReceived(data, length, -1, false);
519
520     // The additional processing can do anything including possibly removing the last
521     // reference to this object; one example of this is 3266216.
522     RefPtr<MainResourceLoader> protect(this);
523
524     m_timeOfLastDataReceived = monotonicallyIncreasingTime();
525
526     documentLoader()->receivedData(data, length);
527
528 #if USE(CONTENT_FILTERING)
529     if (loadWasBlockedBeforeFinishing)
530         cancel();
531 #endif
532 }
533
534 void MainResourceLoader::didFinishLoading(double finishTime)
535 {
536     // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
537     // See <rdar://problem/6304600> for more details.
538 #if !USE(CF)
539     ASSERT(!defersLoading() || InspectorInstrumentation::isDebuggerPaused(m_documentLoader->frame()));
540 #endif
541
542     // The additional processing can do anything including possibly removing the last
543     // reference to this object.
544     RefPtr<MainResourceLoader> protect(this);
545     RefPtr<DocumentLoader> dl = documentLoader();
546
547     if (m_identifierForLoadWithoutResourceLoader) {
548         frameLoader()->notifier()->dispatchDidFinishLoading(documentLoader(), identifier(), finishTime);
549         m_identifierForLoadWithoutResourceLoader = 0;
550     }
551
552 #if USE(CONTENT_FILTERING)
553     if (m_contentFilter && m_contentFilter->needsMoreData()) {
554         m_contentFilter->finishedAddingData();
555
556         int length;
557         const char* data = m_contentFilter->getReplacementData(length);
558         if (data)
559             dataReceived(m_resource.get(), data, length);
560     }
561 #endif
562
563     if (m_loadingMultipartContent)
564         dl->maybeFinishLoadingMultipartContent();
565
566     documentLoader()->timing()->setResponseEnd(finishTime ? finishTime : (m_timeOfLastDataReceived ? m_timeOfLastDataReceived : monotonicallyIncreasingTime()));
567     documentLoader()->finishedLoading();
568
569     // If the document specified an application cache manifest, it violates the author's intent if we store it in the memory cache
570     // and deny the appcache the chance to intercept it in the future, so remove from the memory cache.
571     if (Frame* frame = documentLoader()->frame()) {
572         if (m_resource && frame->document()->hasManifest())
573             memoryCache()->remove(m_resource.get());
574     }
575
576     dl->applicationCacheHost()->finishedLoadingMainResource();
577 }
578
579 void MainResourceLoader::notifyFinished(CachedResource* resource)
580 {
581     ASSERT_UNUSED(resource, m_resource == resource);
582     ASSERT(m_resource);
583     if (!m_resource->errorOccurred() && !m_resource->wasCanceled()) {
584         didFinishLoading(m_resource->loadFinishTime());
585         return;
586     }
587
588     // FIXME: we should fix the design to eliminate the need for a platform ifdef here
589 #if !PLATFORM(CHROMIUM)
590     if (m_documentLoader->request().cachePolicy() == ReturnCacheDataDontLoad && !m_resource->wasCanceled()) {
591         frameLoader()->retryAfterFailedCacheOnlyMainResourceLoad();
592         return;
593     }
594 #endif
595
596     const ResourceError& error = m_resource->resourceError();
597     if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainError(request(), error))
598         return;
599
600     // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
601     // See <rdar://problem/6304600> for more details.
602 #if !USE(CF)
603     ASSERT(!defersLoading());
604 #endif
605     
606     receivedError(error);
607 }
608
609 void MainResourceLoader::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
610 {
611     MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::Loader);
612     info.addMember(m_resource, "resource");
613     info.addMember(m_initialRequest, "initialRequest");
614     info.addMember(m_substituteData, "substituteData");
615     info.addMember(m_dataLoadTimer, "dataLoadTimer");
616     info.addMember(m_documentLoader, "documentLoader");
617 }
618
619 void MainResourceLoader::handleSubstituteDataLoadNow(MainResourceLoaderTimer*)
620 {
621     RefPtr<MainResourceLoader> protect(this);
622
623     KURL url = m_substituteData.responseURL();
624     if (url.isEmpty())
625         url = m_initialRequest.url();
626
627     // Clear the initial request here so that subsequent entries into the
628     // loader will not think there's still a deferred load left to do.
629     m_initialRequest = ResourceRequest();
630         
631     ResourceResponse response(url, m_substituteData.mimeType(), m_substituteData.content()->size(), m_substituteData.textEncoding(), "");
632     responseReceived(0, response);
633 }
634
635 void MainResourceLoader::startDataLoadTimer()
636 {
637     m_dataLoadTimer.startOneShot(0);
638
639 #if HAVE(RUNLOOP_TIMER)
640     if (SchedulePairHashSet* scheduledPairs = m_documentLoader->frame()->page()->scheduledRunLoopPairs())
641         m_dataLoadTimer.schedule(*scheduledPairs);
642 #endif
643 }
644
645 void MainResourceLoader::handleSubstituteDataLoadSoon(const ResourceRequest& r)
646 {
647     m_initialRequest = r;
648     
649     if (m_documentLoader->deferMainResourceDataLoad())
650         startDataLoadTimer();
651     else
652         handleSubstituteDataLoadNow(0);
653 }
654
655 void MainResourceLoader::load(const ResourceRequest& initialRequest, const SubstituteData& substituteData)
656 {
657     // It appears that it is possible for this load to be cancelled and derefenced by the DocumentLoader
658     // in willSendRequest() if loadNow() is called.
659     RefPtr<MainResourceLoader> protect(this);
660
661     m_substituteData = substituteData;
662
663     ASSERT(documentLoader()->timing()->navigationStart());
664     ASSERT(!documentLoader()->timing()->fetchStart());
665     documentLoader()->timing()->markFetchStart();
666     ResourceRequest request(initialRequest);
667
668     // Send this synthetic delegate callback since clients expect it, and
669     // we no longer send the callback from within NSURLConnection for
670     // initial requests.
671     willSendRequest(request, ResourceResponse());
672     ASSERT(!deletionHasBegun());
673
674     // willSendRequest() may lead to our DocumentLoader being detached or cancelling the load via nulling the ResourceRequest.
675     if (!documentLoader()->frame() || request.isNull())
676         return;
677
678     documentLoader()->applicationCacheHost()->maybeLoadMainResource(request, m_substituteData);
679
680     if (m_substituteData.isValid()) {
681         m_identifierForLoadWithoutResourceLoader = m_documentLoader->frame()->page()->progress()->createUniqueIdentifier();
682         frameLoader()->notifier()->assignIdentifierToInitialRequest(m_identifierForLoadWithoutResourceLoader, documentLoader(), request);
683         frameLoader()->notifier()->dispatchWillSendRequest(documentLoader(), m_identifierForLoadWithoutResourceLoader, request, ResourceResponse());
684         handleSubstituteDataLoadSoon(request);
685         return;
686     }
687
688     DEFINE_STATIC_LOCAL(ResourceLoaderOptions, mainResourceLoadOptions,
689         (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck));
690     CachedResourceRequest cachedResourceRequest(request, mainResourceLoadOptions);
691     m_resource = documentLoader()->cachedResourceLoader()->requestMainResource(cachedResourceRequest);
692     if (!m_resource) {
693         documentLoader()->setRequest(ResourceRequest());
694         return;
695     }
696     if (!loader()) {
697         m_identifierForLoadWithoutResourceLoader = m_documentLoader->frame()->page()->progress()->createUniqueIdentifier();
698         frameLoader()->notifier()->assignIdentifierToInitialRequest(m_identifierForLoadWithoutResourceLoader, documentLoader(), request);
699         frameLoader()->notifier()->dispatchWillSendRequest(documentLoader(), m_identifierForLoadWithoutResourceLoader, request, ResourceResponse());
700     }
701     m_resource->addClient(this);
702
703     // A bunch of headers are set when the underlying ResourceLoader is created, and DocumentLoader::m_request needs to include those.
704     if (loader())
705         request = loader()->originalRequest();
706     // If there was a fragment identifier on initialRequest, the cache will have stripped it. DocumentLoader::m_request should include
707     // the fragment identifier, so add that back in.
708     if (equalIgnoringFragmentIdentifier(initialRequest.url(), request.url()))
709         request.setURL(initialRequest.url());
710     documentLoader()->setRequest(request);
711 }
712
713 void MainResourceLoader::setDefersLoading(bool defers)
714 {
715     if (loader())
716         loader()->setDefersLoading(defers);
717 }
718
719 bool MainResourceLoader::defersLoading() const
720 {
721     return loader() ? loader()->defersLoading() : false;
722 }
723
724 void MainResourceLoader::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy)
725 {
726     if (m_resource)
727         m_resource->setDataBufferingPolicy(dataBufferingPolicy);
728 }
729
730 ResourceLoader* MainResourceLoader::loader() const
731
732     return m_resource ? m_resource->loader() : 0;
733 }
734
735 unsigned long MainResourceLoader::identifier() const
736 {
737     ASSERT(!m_identifierForLoadWithoutResourceLoader || !loader() || !loader()->identifier());
738     if (m_identifierForLoadWithoutResourceLoader)
739         return m_identifierForLoadWithoutResourceLoader;
740     if (ResourceLoader* resourceLoader = loader())
741         return resourceLoader->identifier();
742     return 0;
743 }
744
745 }