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