Home | History | Annotate | Download | only in loader
      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 
     57 namespace WebCore {
     58 
     59 MainResourceLoader::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 
     68 MainResourceLoader::~MainResourceLoader()
     69 {
     70 }
     71 
     72 PassRefPtr<MainResourceLoader> MainResourceLoader::create(Frame* frame)
     73 {
     74     return adoptRef(new MainResourceLoader(frame));
     75 }
     76 
     77 void 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 
     99 void 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 
    116 ResourceError MainResourceLoader::interruptionForPolicyChangeError() const
    117 {
    118     return frameLoader()->interruptionForPolicyChangeError(request());
    119 }
    120 
    121 void MainResourceLoader::stopLoadingForPolicyChange()
    122 {
    123     ResourceError error = interruptionForPolicyChangeError();
    124     error.setIsCancellation(true);
    125     cancel(error);
    126 }
    127 
    128 void MainResourceLoader::callContinueAfterNavigationPolicy(void* argument, const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue)
    129 {
    130     static_cast<MainResourceLoader*>(argument)->continueAfterNavigationPolicy(request, shouldContinue);
    131 }
    132 
    133 void 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 
    147 bool 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 
    160 void MainResourceLoader::addData(const char* data, int length, bool allAtOnce)
    161 {
    162     ResourceLoader::addData(data, length, allAtOnce);
    163     documentLoader()->receivedData(data, length);
    164 }
    165 
    166 void 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 
    233 static 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 
    242 void 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 
    314 void MainResourceLoader::callContinueAfterContentPolicy(void* argument, PolicyAction policy)
    315 {
    316     static_cast<MainResourceLoader*>(argument)->continueAfterContentPolicy(policy);
    317 }
    318 
    319 void 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)
    329 void 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 
    351 void 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 
    422 void 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 
    457 void 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 
    483 void 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 
    499 void 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 
    511 void 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 
    527 void 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 
    537 void 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 
    547 bool 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 
    581 bool 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 
    615 void 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