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 "DOMWindow.h"
35#include "Document.h"
36#include "DocumentLoadTiming.h"
37#include "DocumentLoader.h"
38#include "FormState.h"
39#include "Frame.h"
40#include "FrameLoader.h"
41#include "FrameLoaderClient.h"
42#include "HTMLFormElement.h"
43#include "InspectorInstrumentation.h"
44#include "Page.h"
45#if PLATFORM(QT)
46#include "PluginDatabase.h"
47#endif
48#include "ResourceError.h"
49#include "ResourceHandle.h"
50#include "ResourceLoadScheduler.h"
51#include "SchemeRegistry.h"
52#include "Settings.h"
53#include <wtf/CurrentTime.h>
54
55// FIXME: More that is in common with SubresourceLoader should move up into ResourceLoader.
56
57namespace WebCore {
58
59MainResourceLoader::MainResourceLoader(Frame* frame)
60    : ResourceLoader(frame, true, true)
61    , m_dataLoadTimer(this, &MainResourceLoader::handleDataLoadNow)
62    , m_loadingMultipartContent(false)
63    , m_waitingForContentPolicy(false)
64    , m_timeOfLastDataReceived(0.0)
65{
66}
67
68MainResourceLoader::~MainResourceLoader()
69{
70}
71
72PassRefPtr<MainResourceLoader> MainResourceLoader::create(Frame* frame)
73{
74    return adoptRef(new MainResourceLoader(frame));
75}
76
77void MainResourceLoader::receivedError(const ResourceError& error)
78{
79    // Calling receivedMainResourceError will likely result in the last reference to this object to go away.
80    RefPtr<MainResourceLoader> protect(this);
81    RefPtr<Frame> protectFrame(m_frame);
82
83    // It is important that we call FrameLoader::receivedMainResourceError before calling
84    // FrameLoader::didFailToLoad because receivedMainResourceError clears out the relevant
85    // document loaders. Also, receivedMainResourceError ends up calling a FrameLoadDelegate method
86    // and didFailToLoad calls a ResourceLoadDelegate method and they need to be in the correct order.
87    frameLoader()->receivedMainResourceError(error, true);
88
89    if (!cancelled()) {
90        ASSERT(!reachedTerminalState());
91        frameLoader()->notifier()->didFailToLoad(this, error);
92
93        releaseResources();
94    }
95
96    ASSERT(reachedTerminalState());
97}
98
99void MainResourceLoader::didCancel(const ResourceError& error)
100{
101    m_dataLoadTimer.stop();
102
103    // Calling receivedMainResourceError will likely result in the last reference to this object to go away.
104    RefPtr<MainResourceLoader> protect(this);
105
106    if (m_waitingForContentPolicy) {
107        frameLoader()->policyChecker()->cancelCheck();
108        ASSERT(m_waitingForContentPolicy);
109        m_waitingForContentPolicy = false;
110        deref(); // balances ref in didReceiveResponse
111    }
112    frameLoader()->receivedMainResourceError(error, true);
113    ResourceLoader::didCancel(error);
114}
115
116ResourceError MainResourceLoader::interruptionForPolicyChangeError() const
117{
118    return frameLoader()->interruptionForPolicyChangeError(request());
119}
120
121void MainResourceLoader::stopLoadingForPolicyChange()
122{
123    ResourceError error = interruptionForPolicyChangeError();
124    error.setIsCancellation(true);
125    cancel(error);
126}
127
128void MainResourceLoader::callContinueAfterNavigationPolicy(void* argument, const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue)
129{
130    static_cast<MainResourceLoader*>(argument)->continueAfterNavigationPolicy(request, shouldContinue);
131}
132
133void MainResourceLoader::continueAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue)
134{
135    if (!shouldContinue)
136        stopLoadingForPolicyChange();
137    else if (m_substituteData.isValid()) {
138        // A redirect resulted in loading substitute data.
139        ASSERT(documentLoader()->timing()->redirectCount);
140        handle()->cancel();
141        handleDataLoadSoon(request);
142    }
143
144    deref(); // balances ref in willSendRequest
145}
146
147bool MainResourceLoader::isPostOrRedirectAfterPost(const ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
148{
149    if (newRequest.httpMethod() == "POST")
150        return true;
151
152    int status = redirectResponse.httpStatusCode();
153    if (((status >= 301 && status <= 303) || status == 307)
154        && frameLoader()->initialRequest().httpMethod() == "POST")
155        return true;
156
157    return false;
158}
159
160void MainResourceLoader::addData(const char* data, int length, bool allAtOnce)
161{
162    ResourceLoader::addData(data, length, allAtOnce);
163    documentLoader()->receivedData(data, length);
164}
165
166void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
167{
168    // Note that there are no asserts here as there are for the other callbacks. This is due to the
169    // fact that this "callback" is sent when starting every load, and the state of callback
170    // deferrals plays less of a part in this function in preventing the bad behavior deferring
171    // callbacks is meant to prevent.
172    ASSERT(!newRequest.isNull());
173
174    // The additional processing can do anything including possibly removing the last
175    // reference to this object; one example of this is 3266216.
176    RefPtr<MainResourceLoader> protect(this);
177
178    ASSERT(documentLoader()->timing()->fetchStart);
179    if (!redirectResponse.isNull()) {
180        DocumentLoadTiming* documentLoadTiming = documentLoader()->timing();
181
182        // Check if the redirected url is allowed to access the redirecting url's timing information.
183        RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(newRequest.url());
184        if (!securityOrigin->canRequest(redirectResponse.url()))
185            documentLoadTiming->hasCrossOriginRedirect = true;
186
187        documentLoadTiming->redirectCount++;
188        if (!documentLoadTiming->redirectStart)
189            documentLoadTiming->redirectStart = documentLoadTiming->fetchStart;
190        documentLoadTiming->redirectEnd = currentTime();
191        documentLoadTiming->fetchStart = documentLoadTiming->redirectEnd;
192    }
193
194    // Update cookie policy base URL as URL changes, except for subframes, which use the
195    // URL of the main frame which doesn't change when we redirect.
196    if (frameLoader()->isLoadingMainFrame())
197        newRequest.setFirstPartyForCookies(newRequest.url());
198
199    // If we're fielding a redirect in response to a POST, force a load from origin, since
200    // this is a common site technique to return to a page viewing some data that the POST
201    // just modified.
202    // Also, POST requests always load from origin, but this does not affect subresources.
203    if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse))
204        newRequest.setCachePolicy(ReloadIgnoringCacheData);
205
206    ResourceLoader::willSendRequest(newRequest, redirectResponse);
207
208    // Don't set this on the first request. It is set when the main load was started.
209    m_documentLoader->setRequest(newRequest);
210
211    Frame* top = m_frame->tree()->top();
212    if (top != m_frame)
213        frameLoader()->checkIfDisplayInsecureContent(top->document()->securityOrigin(), newRequest.url());
214
215#if ENABLE(OFFLINE_WEB_APPLICATIONS)
216    if (!redirectResponse.isNull()) {
217        // We checked application cache for initial URL, now we need to check it for redirected one.
218        ASSERT(!m_substituteData.isValid());
219        documentLoader()->applicationCacheHost()->maybeLoadMainResourceForRedirect(newRequest, m_substituteData);
220    }
221#endif
222
223    // FIXME: Ideally we'd stop the I/O until we hear back from the navigation policy delegate
224    // listener. But there's no way to do that in practice. So instead we cancel later if the
225    // listener tells us to. In practice that means the navigation policy needs to be decided
226    // synchronously for these redirect cases.
227    if (!redirectResponse.isNull()) {
228        ref(); // balanced by deref in continueAfterNavigationPolicy
229        frameLoader()->policyChecker()->checkNavigationPolicy(newRequest, callContinueAfterNavigationPolicy, this);
230    }
231}
232
233static bool shouldLoadAsEmptyDocument(const KURL& url)
234{
235#if PLATFORM(TORCHMOBILE)
236    return url.isEmpty() || (url.protocolIs("about") && equalIgnoringRef(url, blankURL()));
237#else
238    return url.isEmpty() || SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(url.protocol());
239#endif
240}
241
242void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, const ResourceResponse& r)
243{
244    KURL url = request().url();
245    const String& mimeType = r.mimeType();
246
247    switch (contentPolicy) {
248    case PolicyUse: {
249        // Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255).
250        bool isRemoteWebArchive = equalIgnoringCase("application/x-webarchive", mimeType) && !m_substituteData.isValid() && !url.isLocalFile();
251        if (!frameLoader()->canShowMIMEType(mimeType) || isRemoteWebArchive) {
252            frameLoader()->policyChecker()->cannotShowMIMEType(r);
253            // Check reachedTerminalState since the load may have already been cancelled inside of _handleUnimplementablePolicyWithErrorCode::.
254            if (!reachedTerminalState())
255                stopLoadingForPolicyChange();
256            return;
257        }
258        break;
259    }
260
261    case PolicyDownload:
262        // m_handle can be null, e.g. when loading a substitute resource from application cache.
263        if (!m_handle) {
264            receivedError(cannotShowURLError());
265            return;
266        }
267        InspectorInstrumentation::continueWithPolicyDownload(m_frame.get(), documentLoader(), identifier(), r);
268        frameLoader()->client()->download(m_handle.get(), request(), m_handle.get()->firstRequest(), r);
269        // It might have gone missing
270        if (frameLoader())
271            receivedError(interruptionForPolicyChangeError());
272        return;
273
274    case PolicyIgnore:
275        InspectorInstrumentation::continueWithPolicyIgnore(m_frame.get(), documentLoader(), identifier(), r);
276        stopLoadingForPolicyChange();
277        return;
278
279    default:
280        ASSERT_NOT_REACHED();
281    }
282
283    RefPtr<MainResourceLoader> protect(this);
284
285    if (r.isHTTP()) {
286        int status = r.httpStatusCode();
287        if (status < 200 || status >= 300) {
288            bool hostedByObject = frameLoader()->isHostedByObjectElement();
289
290            frameLoader()->handleFallbackContent();
291            // object elements are no longer rendered after we fallback, so don't
292            // keep trying to process data from their load
293
294            if (hostedByObject)
295                cancel();
296        }
297    }
298
299    // we may have cancelled this load as part of switching to fallback content
300    if (!reachedTerminalState())
301        ResourceLoader::didReceiveResponse(r);
302
303    if (frameLoader() && !frameLoader()->isStopping()) {
304        if (m_substituteData.isValid()) {
305            if (m_substituteData.content()->size())
306                didReceiveData(m_substituteData.content()->data(), m_substituteData.content()->size(), m_substituteData.content()->size(), true);
307            if (frameLoader() && !frameLoader()->isStopping())
308                didFinishLoading(0);
309        } else if (shouldLoadAsEmptyDocument(url) || frameLoader()->representationExistsForURLScheme(url.protocol()))
310            didFinishLoading(0);
311    }
312}
313
314void MainResourceLoader::callContinueAfterContentPolicy(void* argument, PolicyAction policy)
315{
316    static_cast<MainResourceLoader*>(argument)->continueAfterContentPolicy(policy);
317}
318
319void MainResourceLoader::continueAfterContentPolicy(PolicyAction policy)
320{
321    ASSERT(m_waitingForContentPolicy);
322    m_waitingForContentPolicy = false;
323    if (frameLoader() && !frameLoader()->isStopping())
324        continueAfterContentPolicy(policy, m_response);
325    deref(); // balances ref in didReceiveResponse
326}
327
328#if PLATFORM(QT)
329void MainResourceLoader::substituteMIMETypeFromPluginDatabase(const ResourceResponse& r)
330{
331    if (!m_frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin))
332        return;
333
334    String filename = r.url().lastPathComponent();
335    if (filename.endsWith("/"))
336        return;
337
338    size_t extensionPos = filename.reverseFind('.');
339    if (extensionPos == notFound)
340        return;
341
342    String extension = filename.substring(extensionPos + 1);
343    String mimeType = PluginDatabase::installedPlugins()->MIMETypeForExtension(extension);
344    if (!mimeType.isEmpty()) {
345        ResourceResponse* response = const_cast<ResourceResponse*>(&r);
346        response->setMimeType(mimeType);
347    }
348}
349#endif
350
351void MainResourceLoader::didReceiveResponse(const ResourceResponse& r)
352{
353#if ENABLE(OFFLINE_WEB_APPLICATIONS)
354    if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainResponse(request(), r))
355        return;
356#endif
357
358    HTTPHeaderMap::const_iterator it = r.httpHeaderFields().find(AtomicString("x-frame-options"));
359    if (it != r.httpHeaderFields().end()) {
360        String content = it->second;
361        if (m_frame->loader()->shouldInterruptLoadForXFrameOptions(content, r.url())) {
362            InspectorInstrumentation::continueAfterXFrameOptionsDenied(m_frame.get(), documentLoader(), identifier(), r);
363            DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to display document because display forbidden by X-Frame-Options.\n"));
364            m_frame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, consoleMessage, 1, String());
365
366            cancel();
367            return;
368        }
369    }
370
371    // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
372    // See <rdar://problem/6304600> for more details.
373#if !USE(CF)
374    ASSERT(shouldLoadAsEmptyDocument(r.url()) || !defersLoading());
375#endif
376
377#if PLATFORM(QT)
378    if (r.mimeType() == "application/octet-stream")
379        substituteMIMETypeFromPluginDatabase(r);
380#endif
381
382    if (m_loadingMultipartContent) {
383        frameLoader()->setupForReplaceByMIMEType(r.mimeType());
384        clearResourceData();
385    }
386
387    if (r.isMultipart())
388        m_loadingMultipartContent = true;
389
390    // The additional processing can do anything including possibly removing the last
391    // reference to this object; one example of this is 3266216.
392    RefPtr<MainResourceLoader> protect(this);
393
394    m_documentLoader->setResponse(r);
395
396    m_response = r;
397
398    ASSERT(!m_waitingForContentPolicy);
399    m_waitingForContentPolicy = true;
400    ref(); // balanced by deref in continueAfterContentPolicy and didCancel
401
402    ASSERT(frameLoader()->activeDocumentLoader());
403
404    // Always show content with valid substitute data.
405    if (frameLoader()->activeDocumentLoader()->substituteData().isValid()) {
406        callContinueAfterContentPolicy(this, PolicyUse);
407        return;
408    }
409
410#if ENABLE(FTPDIR)
411    // Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it
412    Settings* settings = m_frame->settings();
413    if (settings && settings->forceFTPDirectoryListings() && m_response.mimeType() == "application/x-ftp-directory") {
414        callContinueAfterContentPolicy(this, PolicyUse);
415        return;
416    }
417#endif
418
419    frameLoader()->policyChecker()->checkContentPolicy(m_response, callContinueAfterContentPolicy, this);
420}
421
422void MainResourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)
423{
424    ASSERT(data);
425    ASSERT(length != 0);
426
427    ASSERT(!m_response.isNull());
428
429#if USE(CFNETWORK) || (PLATFORM(MAC) && !defined(BUILDING_ON_TIGER))
430    // Workaround for <rdar://problem/6060782>
431    if (m_response.isNull()) {
432        m_response = ResourceResponse(KURL(), "text/html", 0, String(), String());
433        if (DocumentLoader* documentLoader = frameLoader()->activeDocumentLoader())
434            documentLoader->setResponse(m_response);
435    }
436#endif
437
438    // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
439    // See <rdar://problem/6304600> for more details.
440#if !USE(CF)
441    ASSERT(!defersLoading());
442#endif
443
444 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
445    documentLoader()->applicationCacheHost()->mainResourceDataReceived(data, length, encodedDataLength, allAtOnce);
446#endif
447
448    // The additional processing can do anything including possibly removing the last
449    // reference to this object; one example of this is 3266216.
450    RefPtr<MainResourceLoader> protect(this);
451
452    m_timeOfLastDataReceived = currentTime();
453
454    ResourceLoader::didReceiveData(data, length, encodedDataLength, allAtOnce);
455}
456
457void MainResourceLoader::didFinishLoading(double finishTime)
458{
459    // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
460    // See <rdar://problem/6304600> for more details.
461#if !USE(CF)
462    ASSERT(shouldLoadAsEmptyDocument(frameLoader()->activeDocumentLoader()->url()) || !defersLoading());
463#endif
464
465    // The additional processing can do anything including possibly removing the last
466    // reference to this object.
467    RefPtr<MainResourceLoader> protect(this);
468
469#if ENABLE(OFFLINE_WEB_APPLICATIONS)
470    RefPtr<DocumentLoader> dl = documentLoader();
471#endif
472
473    ASSERT(!documentLoader()->timing()->responseEnd);
474    documentLoader()->timing()->responseEnd = finishTime ? finishTime : (m_timeOfLastDataReceived ? m_timeOfLastDataReceived : currentTime());
475    frameLoader()->finishedLoading();
476    ResourceLoader::didFinishLoading(finishTime);
477
478#if ENABLE(OFFLINE_WEB_APPLICATIONS)
479    dl->applicationCacheHost()->finishedLoadingMainResource();
480#endif
481}
482
483void MainResourceLoader::didFail(const ResourceError& error)
484{
485#if ENABLE(OFFLINE_WEB_APPLICATIONS)
486    if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainError(request(), error))
487        return;
488#endif
489
490    // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
491    // See <rdar://problem/6304600> for more details.
492#if !USE(CF)
493    ASSERT(!defersLoading());
494#endif
495
496    receivedError(error);
497}
498
499void MainResourceLoader::handleEmptyLoad(const KURL& url, bool forURLScheme)
500{
501    String mimeType;
502    if (forURLScheme)
503        mimeType = frameLoader()->generatedMIMETypeForURLScheme(url.protocol());
504    else
505        mimeType = "text/html";
506
507    ResourceResponse response(url, mimeType, 0, String(), String());
508    didReceiveResponse(response);
509}
510
511void MainResourceLoader::handleDataLoadNow(MainResourceLoaderTimer*)
512{
513    RefPtr<MainResourceLoader> protect(this);
514
515    KURL url = m_substituteData.responseURL();
516    if (url.isEmpty())
517        url = m_initialRequest.url();
518
519    // Clear the initial request here so that subsequent entries into the
520    // loader will not think there's still a deferred load left to do.
521    m_initialRequest = ResourceRequest();
522
523    ResourceResponse response(url, m_substituteData.mimeType(), m_substituteData.content()->size(), m_substituteData.textEncoding(), "");
524    didReceiveResponse(response);
525}
526
527void MainResourceLoader::startDataLoadTimer()
528{
529    m_dataLoadTimer.startOneShot(0);
530
531#if HAVE(RUNLOOP_TIMER)
532    if (SchedulePairHashSet* scheduledPairs = m_frame->page()->scheduledRunLoopPairs())
533        m_dataLoadTimer.schedule(*scheduledPairs);
534#endif
535}
536
537void MainResourceLoader::handleDataLoadSoon(const ResourceRequest& r)
538{
539    m_initialRequest = r;
540
541    if (m_documentLoader->deferMainResourceDataLoad())
542        startDataLoadTimer();
543    else
544        handleDataLoadNow(0);
545}
546
547bool MainResourceLoader::loadNow(ResourceRequest& r)
548{
549    bool shouldLoadEmptyBeforeRedirect = shouldLoadAsEmptyDocument(r.url());
550
551    ASSERT(!m_handle);
552    ASSERT(shouldLoadEmptyBeforeRedirect || !defersLoading());
553
554    // Send this synthetic delegate callback since clients expect it, and
555    // we no longer send the callback from within NSURLConnection for
556    // initial requests.
557    willSendRequest(r, ResourceResponse());
558
559    // <rdar://problem/4801066>
560    // willSendRequest() is liable to make the call to frameLoader() return NULL, so we need to check that here
561    if (!frameLoader())
562        return false;
563
564    const KURL& url = r.url();
565    bool shouldLoadEmpty = shouldLoadAsEmptyDocument(url) && !m_substituteData.isValid();
566
567    if (shouldLoadEmptyBeforeRedirect && !shouldLoadEmpty && defersLoading())
568        return true;
569
570    resourceLoadScheduler()->addMainResourceLoad(this);
571    if (m_substituteData.isValid())
572        handleDataLoadSoon(r);
573    else if (shouldLoadEmpty || frameLoader()->representationExistsForURLScheme(url.protocol()))
574        handleEmptyLoad(url, !shouldLoadEmpty);
575    else
576        m_handle = ResourceHandle::create(m_frame->loader()->networkingContext(), r, this, false, true);
577
578    return false;
579}
580
581bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData)
582{
583    ASSERT(!m_handle);
584
585    m_substituteData = substituteData;
586
587    ASSERT(documentLoader()->timing()->navigationStart);
588    ASSERT(!documentLoader()->timing()->fetchStart);
589    documentLoader()->timing()->fetchStart = currentTime();
590    ResourceRequest request(r);
591
592#if ENABLE(OFFLINE_WEB_APPLICATIONS)
593    documentLoader()->applicationCacheHost()->maybeLoadMainResource(request, m_substituteData);
594#endif
595
596    bool defer = defersLoading();
597    if (defer) {
598        bool shouldLoadEmpty = shouldLoadAsEmptyDocument(request.url());
599        if (shouldLoadEmpty)
600            defer = false;
601    }
602    if (!defer) {
603        if (loadNow(request)) {
604            // Started as an empty document, but was redirected to something non-empty.
605            ASSERT(defersLoading());
606            defer = true;
607        }
608    }
609    if (defer)
610        m_initialRequest = request;
611
612    return true;
613}
614
615void MainResourceLoader::setDefersLoading(bool defers)
616{
617    ResourceLoader::setDefersLoading(defers);
618
619    if (defers) {
620        if (m_dataLoadTimer.isActive())
621            m_dataLoadTimer.stop();
622    } else {
623        if (m_initialRequest.isNull())
624            return;
625
626        if (m_substituteData.isValid() && m_documentLoader->deferMainResourceDataLoad())
627            startDataLoadTimer();
628        else {
629            ResourceRequest r(m_initialRequest);
630            m_initialRequest = ResourceRequest();
631            loadNow(r);
632        }
633    }
634}
635
636}
637