Home | History | Annotate | Download | only in loader
      1 /*
      2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 #include "DocumentLoader.h"
     31 
     32 #include "ApplicationCacheHost.h"
     33 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
     34 #include "ArchiveFactory.h"
     35 #include "ArchiveResourceCollection.h"
     36 #else
     37 #include "SubstituteResource.h"
     38 #endif
     39 #include "CachedPage.h"
     40 #include "DocLoader.h"
     41 #include "Document.h"
     42 #include "Event.h"
     43 #include "Frame.h"
     44 #include "FrameLoader.h"
     45 #include "FrameTree.h"
     46 #include "HistoryItem.h"
     47 #include "Logging.h"
     48 #include "MainResourceLoader.h"
     49 #include "Page.h"
     50 #include "PlatformString.h"
     51 #include "Settings.h"
     52 #include "SharedBuffer.h"
     53 #include "XMLTokenizer.h"
     54 
     55 #include <wtf/Assertions.h>
     56 #include <wtf/unicode/Unicode.h>
     57 
     58 namespace WebCore {
     59 
     60 static void cancelAll(const ResourceLoaderSet& loaders)
     61 {
     62     const ResourceLoaderSet copy = loaders;
     63     ResourceLoaderSet::const_iterator end = copy.end();
     64     for (ResourceLoaderSet::const_iterator it = copy.begin(); it != end; ++it)
     65         (*it)->cancel();
     66 }
     67 
     68 static void setAllDefersLoading(const ResourceLoaderSet& loaders, bool defers)
     69 {
     70     const ResourceLoaderSet copy = loaders;
     71     ResourceLoaderSet::const_iterator end = copy.end();
     72     for (ResourceLoaderSet::const_iterator it = copy.begin(); it != end; ++it)
     73         (*it)->setDefersLoading(defers);
     74 }
     75 
     76 DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData& substituteData)
     77     : m_deferMainResourceDataLoad(true)
     78     , m_frame(0)
     79     , m_originalRequest(req)
     80     , m_substituteData(substituteData)
     81     , m_originalRequestCopy(req)
     82     , m_request(req)
     83     , m_committed(false)
     84     , m_isStopping(false)
     85     , m_loading(false)
     86     , m_gotFirstByte(false)
     87     , m_primaryLoadComplete(false)
     88     , m_isClientRedirect(false)
     89     , m_stopRecordingResponses(false)
     90     , m_substituteResourceDeliveryTimer(this, &DocumentLoader::substituteResourceDeliveryTimerFired)
     91     , m_didCreateGlobalHistoryEntry(false)
     92 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
     93     , m_applicationCacheHost(new ApplicationCacheHost(this))
     94 #endif
     95 {
     96 }
     97 
     98 FrameLoader* DocumentLoader::frameLoader() const
     99 {
    100     if (!m_frame)
    101         return 0;
    102     return m_frame->loader();
    103 }
    104 
    105 DocumentLoader::~DocumentLoader()
    106 {
    107     ASSERT(!m_frame || frameLoader()->activeDocumentLoader() != this || !frameLoader()->isLoading());
    108 }
    109 
    110 PassRefPtr<SharedBuffer> DocumentLoader::mainResourceData() const
    111 {
    112     if (m_mainResourceData)
    113         return m_mainResourceData;
    114     if (m_mainResourceLoader)
    115         return m_mainResourceLoader->resourceData();
    116     return 0;
    117 }
    118 
    119 const ResourceRequest& DocumentLoader::originalRequest() const
    120 {
    121     return m_originalRequest;
    122 }
    123 
    124 const ResourceRequest& DocumentLoader::originalRequestCopy() const
    125 {
    126     return m_originalRequestCopy;
    127 }
    128 
    129 const ResourceRequest& DocumentLoader::request() const
    130 {
    131     return m_request;
    132 }
    133 
    134 ResourceRequest& DocumentLoader::request()
    135 {
    136     return m_request;
    137 }
    138 
    139 const KURL& DocumentLoader::url() const
    140 {
    141     return request().url();
    142 }
    143 
    144 void DocumentLoader::replaceRequestURLForSameDocumentNavigation(const KURL& url)
    145 {
    146     m_originalRequestCopy.setURL(url);
    147     m_request.setURL(url);
    148 }
    149 
    150 void DocumentLoader::setRequest(const ResourceRequest& req)
    151 {
    152     // Replacing an unreachable URL with alternate content looks like a server-side
    153     // redirect at this point, but we can replace a committed dataSource.
    154     bool handlingUnreachableURL = false;
    155 
    156     handlingUnreachableURL = m_substituteData.isValid() && !m_substituteData.failingURL().isEmpty();
    157 
    158     if (handlingUnreachableURL)
    159         m_committed = false;
    160 
    161     // We should never be getting a redirect callback after the data
    162     // source is committed, except in the unreachable URL case. It
    163     // would be a WebFoundation bug if it sent a redirect callback after commit.
    164     ASSERT(!m_committed);
    165 
    166     KURL oldURL = m_request.url();
    167     m_request = req;
    168 
    169     // Only send webView:didReceiveServerRedirectForProvisionalLoadForFrame: if URL changed.
    170     // Also, don't send it when replacing unreachable URLs with alternate content.
    171     if (!handlingUnreachableURL && oldURL != req.url())
    172         frameLoader()->didReceiveServerRedirectForProvisionalLoadForFrame();
    173 }
    174 
    175 void DocumentLoader::setMainDocumentError(const ResourceError& error)
    176 {
    177     m_mainDocumentError = error;
    178     frameLoader()->setMainDocumentError(this, error);
    179  }
    180 
    181 void DocumentLoader::clearErrors()
    182 {
    183     m_mainDocumentError = ResourceError();
    184 }
    185 
    186 void DocumentLoader::mainReceivedError(const ResourceError& error, bool isComplete)
    187 {
    188     ASSERT(!error.isNull());
    189 
    190 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
    191     m_applicationCacheHost->failedLoadingMainResource();
    192 #endif
    193 
    194     if (!frameLoader())
    195         return;
    196     setMainDocumentError(error);
    197     if (isComplete)
    198         frameLoader()->mainReceivedCompleteError(this, error);
    199 }
    200 
    201 // Cancels the data source's pending loads.  Conceptually, a data source only loads
    202 // one document at a time, but one document may have many related resources.
    203 // stopLoading will stop all loads initiated by the data source,
    204 // but not loads initiated by child frames' data sources -- that's the WebFrame's job.
    205 void DocumentLoader::stopLoading(DatabasePolicy databasePolicy)
    206 {
    207     // In some rare cases, calling FrameLoader::stopLoading could set m_loading to false.
    208     // (This can happen when there's a single XMLHttpRequest currently loading and stopLoading causes it
    209     // to stop loading. Because of this, we need to save it so we don't return early.
    210     bool loading = m_loading;
    211 
    212     if (m_committed) {
    213         // Attempt to stop the frame if the document loader is loading, or if it is done loading but
    214         // still  parsing. Failure to do so can cause a world leak.
    215         Document* doc = m_frame->document();
    216 
    217         if (loading || doc->parsing())
    218             m_frame->loader()->stopLoading(UnloadEventPolicyNone, databasePolicy);
    219     }
    220 
    221     // Always cancel multipart loaders
    222     cancelAll(m_multipartSubresourceLoaders);
    223 
    224     if (!loading)
    225         return;
    226 
    227     RefPtr<Frame> protectFrame(m_frame);
    228     RefPtr<DocumentLoader> protectLoader(this);
    229 
    230     m_isStopping = true;
    231 
    232     FrameLoader* frameLoader = DocumentLoader::frameLoader();
    233 
    234     if (m_mainResourceLoader)
    235         // Stop the main resource loader and let it send the cancelled message.
    236         m_mainResourceLoader->cancel();
    237     else if (!m_subresourceLoaders.isEmpty())
    238         // The main resource loader already finished loading. Set the cancelled error on the
    239         // document and let the subresourceLoaders send individual cancelled messages below.
    240         setMainDocumentError(frameLoader->cancelledError(m_request));
    241     else
    242         // If there are no resource loaders, we need to manufacture a cancelled message.
    243         // (A back/forward navigation has no resource loaders because its resources are cached.)
    244         mainReceivedError(frameLoader->cancelledError(m_request), true);
    245 
    246     stopLoadingSubresources();
    247     stopLoadingPlugIns();
    248 
    249     m_isStopping = false;
    250 }
    251 
    252 void DocumentLoader::setupForReplace()
    253 {
    254     frameLoader()->setupForReplace();
    255     m_committed = false;
    256 }
    257 
    258 void DocumentLoader::commitIfReady()
    259 {
    260     if (m_gotFirstByte && !m_committed) {
    261         m_committed = true;
    262         frameLoader()->commitProvisionalLoad(0);
    263     }
    264 }
    265 
    266 void DocumentLoader::finishedLoading()
    267 {
    268     m_gotFirstByte = true;
    269     commitIfReady();
    270     if (FrameLoader* loader = frameLoader()) {
    271         loader->finishedLoadingDocument(this);
    272         loader->end();
    273     }
    274 }
    275 
    276 void DocumentLoader::commitLoad(const char* data, int length)
    277 {
    278     // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource
    279     // by starting a new load, so retain temporarily.
    280     RefPtr<DocumentLoader> protect(this);
    281 
    282     commitIfReady();
    283     if (FrameLoader* frameLoader = DocumentLoader::frameLoader())
    284         frameLoader->committedLoad(this, data, length);
    285 }
    286 
    287 bool DocumentLoader::doesProgressiveLoad(const String& MIMEType) const
    288 {
    289     return !frameLoader()->isReplacing() || MIMEType == "text/html";
    290 }
    291 
    292 void DocumentLoader::receivedData(const char* data, int length)
    293 {
    294     m_gotFirstByte = true;
    295     if (doesProgressiveLoad(m_response.mimeType()))
    296         commitLoad(data, length);
    297 }
    298 
    299 void DocumentLoader::setupForReplaceByMIMEType(const String& newMIMEType)
    300 {
    301     if (!m_gotFirstByte)
    302         return;
    303 
    304     String oldMIMEType = m_response.mimeType();
    305 
    306     if (!doesProgressiveLoad(oldMIMEType)) {
    307         frameLoader()->revertToProvisional(this);
    308         setupForReplace();
    309         RefPtr<SharedBuffer> resourceData = mainResourceData();
    310         commitLoad(resourceData->data(), resourceData->size());
    311     }
    312 
    313     frameLoader()->finishedLoadingDocument(this);
    314     m_frame->loader()->end();
    315 
    316     frameLoader()->setReplacing();
    317     m_gotFirstByte = false;
    318 
    319     if (doesProgressiveLoad(newMIMEType)) {
    320         frameLoader()->revertToProvisional(this);
    321         setupForReplace();
    322     }
    323 
    324     stopLoadingSubresources();
    325     stopLoadingPlugIns();
    326 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
    327     clearArchiveResources();
    328 #endif
    329 }
    330 
    331 void DocumentLoader::updateLoading()
    332 {
    333     if (!m_frame) {
    334         setLoading(false);
    335         return;
    336     }
    337     ASSERT(this == frameLoader()->activeDocumentLoader());
    338     setLoading(frameLoader()->isLoading());
    339 }
    340 
    341 void DocumentLoader::setFrame(Frame* frame)
    342 {
    343     if (m_frame == frame)
    344         return;
    345     ASSERT(frame && !m_frame);
    346     m_frame = frame;
    347     attachToFrame();
    348 }
    349 
    350 void DocumentLoader::attachToFrame()
    351 {
    352     ASSERT(m_frame);
    353 }
    354 
    355 void DocumentLoader::detachFromFrame()
    356 {
    357     ASSERT(m_frame);
    358 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
    359     m_applicationCacheHost->setDOMApplicationCache(0);
    360 #endif
    361     m_frame = 0;
    362 }
    363 
    364 void DocumentLoader::prepareForLoadStart()
    365 {
    366     ASSERT(!m_isStopping);
    367     setPrimaryLoadComplete(false);
    368     ASSERT(frameLoader());
    369     clearErrors();
    370 
    371     setLoading(true);
    372 
    373     frameLoader()->prepareForLoadStart();
    374 }
    375 
    376 void DocumentLoader::setPrimaryLoadComplete(bool flag)
    377 {
    378     m_primaryLoadComplete = flag;
    379     if (flag) {
    380         if (m_mainResourceLoader) {
    381             m_mainResourceData = m_mainResourceLoader->resourceData();
    382             m_mainResourceLoader = 0;
    383         }
    384 
    385         if (this == frameLoader()->activeDocumentLoader())
    386             updateLoading();
    387     }
    388 }
    389 
    390 bool DocumentLoader::isLoadingInAPISense() const
    391 {
    392     // Once a frame has loaded, we no longer need to consider subresources,
    393     // but we still need to consider subframes.
    394     if (frameLoader()->state() != FrameStateComplete) {
    395         if (!m_primaryLoadComplete && isLoading())
    396             return true;
    397         if (!m_subresourceLoaders.isEmpty())
    398             return true;
    399         Document* doc = m_frame->document();
    400         if (doc->docLoader()->requestCount())
    401             return true;
    402         if (Tokenizer* tok = doc->tokenizer())
    403             if (tok->processingData())
    404                 return true;
    405     }
    406     return frameLoader()->subframeIsLoading();
    407 }
    408 
    409 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
    410 void DocumentLoader::addAllArchiveResources(Archive* archive)
    411 {
    412     if (!m_archiveResourceCollection)
    413         m_archiveResourceCollection.set(new ArchiveResourceCollection);
    414 
    415     ASSERT(archive);
    416     if (!archive)
    417         return;
    418 
    419     m_archiveResourceCollection->addAllResources(archive);
    420 }
    421 
    422 // FIXME: Adding a resource directly to a DocumentLoader/ArchiveResourceCollection seems like bad design, but is API some apps rely on.
    423 // Can we change the design in a manner that will let us deprecate that API without reducing functionality of those apps?
    424 void DocumentLoader::addArchiveResource(PassRefPtr<ArchiveResource> resource)
    425 {
    426     if (!m_archiveResourceCollection)
    427         m_archiveResourceCollection.set(new ArchiveResourceCollection);
    428 
    429     ASSERT(resource);
    430     if (!resource)
    431         return;
    432 
    433     m_archiveResourceCollection->addResource(resource);
    434 }
    435 
    436 ArchiveResource* DocumentLoader::archiveResourceForURL(const KURL& url) const
    437 {
    438     if (!m_archiveResourceCollection)
    439         return 0;
    440 
    441     ArchiveResource* resource = m_archiveResourceCollection->archiveResourceForURL(url);
    442 
    443     return resource && !resource->shouldIgnoreWhenUnarchiving() ? resource : 0;
    444 }
    445 
    446 PassRefPtr<Archive> DocumentLoader::popArchiveForSubframe(const String& frameName)
    447 {
    448     return m_archiveResourceCollection ? m_archiveResourceCollection->popSubframeArchive(frameName) : 0;
    449 }
    450 
    451 void DocumentLoader::clearArchiveResources()
    452 {
    453     m_archiveResourceCollection.clear();
    454     m_substituteResourceDeliveryTimer.stop();
    455 }
    456 
    457 void DocumentLoader::setParsedArchiveData(PassRefPtr<SharedBuffer> data)
    458 {
    459     m_parsedArchiveData = data;
    460 }
    461 
    462 SharedBuffer* DocumentLoader::parsedArchiveData() const
    463 {
    464     return m_parsedArchiveData.get();
    465 }
    466 
    467 PassRefPtr<ArchiveResource> DocumentLoader::mainResource() const
    468 {
    469     const ResourceResponse& r = response();
    470     RefPtr<SharedBuffer> mainResourceBuffer = mainResourceData();
    471     if (!mainResourceBuffer)
    472         mainResourceBuffer = SharedBuffer::create();
    473 
    474     return ArchiveResource::create(mainResourceBuffer, r.url(), r.mimeType(), r.textEncodingName(), frame()->tree()->name());
    475 }
    476 
    477 PassRefPtr<ArchiveResource> DocumentLoader::subresource(const KURL& url) const
    478 {
    479     if (!isCommitted())
    480         return 0;
    481 
    482     CachedResource* resource = m_frame->document()->docLoader()->cachedResource(url);
    483     if (!resource || !resource->isLoaded())
    484         return archiveResourceForURL(url);
    485 
    486     // FIXME: This has the side effect of making the resource non-purgeable.
    487     // It would be better if it didn't have this permanent effect.
    488     if (!resource->makePurgeable(false))
    489         return 0;
    490 
    491     RefPtr<SharedBuffer> data = resource->data();
    492     if (!data)
    493         return 0;
    494 
    495     return ArchiveResource::create(data.release(), url, resource->response());
    496 }
    497 
    498 void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subresources) const
    499 {
    500     if (!isCommitted())
    501         return;
    502 
    503     Document* document = m_frame->document();
    504 
    505     const DocLoader::DocumentResourceMap& allResources = document->docLoader()->allCachedResources();
    506     DocLoader::DocumentResourceMap::const_iterator end = allResources.end();
    507     for (DocLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) {
    508         RefPtr<ArchiveResource> subresource = this->subresource(KURL(ParsedURLString, it->second->url()));
    509         if (subresource)
    510             subresources.append(subresource.release());
    511     }
    512 
    513     return;
    514 }
    515 #endif
    516 
    517 void DocumentLoader::deliverSubstituteResourcesAfterDelay()
    518 {
    519     if (m_pendingSubstituteResources.isEmpty())
    520         return;
    521     ASSERT(m_frame && m_frame->page());
    522     if (m_frame->page()->defersLoading())
    523         return;
    524     if (!m_substituteResourceDeliveryTimer.isActive())
    525         m_substituteResourceDeliveryTimer.startOneShot(0);
    526 }
    527 
    528 void DocumentLoader::substituteResourceDeliveryTimerFired(Timer<DocumentLoader>*)
    529 {
    530     if (m_pendingSubstituteResources.isEmpty())
    531         return;
    532     ASSERT(m_frame && m_frame->page());
    533     if (m_frame->page()->defersLoading())
    534         return;
    535 
    536     SubstituteResourceMap copy;
    537     copy.swap(m_pendingSubstituteResources);
    538 
    539     SubstituteResourceMap::const_iterator end = copy.end();
    540     for (SubstituteResourceMap::const_iterator it = copy.begin(); it != end; ++it) {
    541         RefPtr<ResourceLoader> loader = it->first;
    542         SubstituteResource* resource = it->second.get();
    543 
    544         if (resource) {
    545             SharedBuffer* data = resource->data();
    546 
    547             loader->didReceiveResponse(resource->response());
    548             loader->didReceiveData(data->data(), data->size(), data->size(), true);
    549             loader->didFinishLoading();
    550         } else {
    551             // A null resource means that we should fail the load.
    552             // FIXME: Maybe we should use another error here - something like "not in cache".
    553             loader->didFail(loader->cannotShowURLError());
    554         }
    555     }
    556 }
    557 
    558 #ifndef NDEBUG
    559 bool DocumentLoader::isSubstituteLoadPending(ResourceLoader* loader) const
    560 {
    561     return m_pendingSubstituteResources.contains(loader);
    562 }
    563 #endif
    564 
    565 void DocumentLoader::cancelPendingSubstituteLoad(ResourceLoader* loader)
    566 {
    567     if (m_pendingSubstituteResources.isEmpty())
    568         return;
    569     m_pendingSubstituteResources.remove(loader);
    570     if (m_pendingSubstituteResources.isEmpty())
    571         m_substituteResourceDeliveryTimer.stop();
    572 }
    573 
    574 #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size
    575 bool DocumentLoader::scheduleArchiveLoad(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL)
    576 {
    577     ArchiveResource* resource = 0;
    578 
    579     if (request.url() == originalURL)
    580         resource = archiveResourceForURL(originalURL);
    581 
    582     if (!resource) {
    583         // WebArchiveDebugMode means we fail loads instead of trying to fetch them from the network if they're not in the archive.
    584         bool shouldFailLoad = m_frame->settings()->webArchiveDebugModeEnabled() && ArchiveFactory::isArchiveMimeType(responseMIMEType());
    585 
    586         if (!shouldFailLoad)
    587             return false;
    588     }
    589 
    590     m_pendingSubstituteResources.set(loader, resource);
    591     deliverSubstituteResourcesAfterDelay();
    592 
    593     return true;
    594 }
    595 #endif
    596 
    597 void DocumentLoader::addResponse(const ResourceResponse& r)
    598 {
    599     if (!m_stopRecordingResponses)
    600         m_responses.append(r);
    601 }
    602 
    603 void DocumentLoader::stopRecordingResponses()
    604 {
    605     m_stopRecordingResponses = true;
    606 }
    607 
    608 void DocumentLoader::setTitle(const String& title)
    609 {
    610     if (title.isEmpty())
    611         return;
    612 
    613     if (m_pageTitle != title) {
    614         frameLoader()->willChangeTitle(this);
    615         m_pageTitle = title;
    616         frameLoader()->didChangeTitle(this);
    617     }
    618 }
    619 
    620 KURL DocumentLoader::urlForHistory() const
    621 {
    622     // Return the URL to be used for history and B/F list.
    623     // Returns nil for WebDataProtocol URLs that aren't alternates
    624     // for unreachable URLs, because these can't be stored in history.
    625     if (m_substituteData.isValid())
    626         return unreachableURL();
    627 
    628     return m_originalRequestCopy.url();
    629 }
    630 
    631 bool DocumentLoader::urlForHistoryReflectsFailure() const
    632 {
    633     return m_substituteData.isValid() || m_response.httpStatusCode() >= 400;
    634 }
    635 
    636 const KURL& DocumentLoader::originalURL() const
    637 {
    638     return m_originalRequestCopy.url();
    639 }
    640 
    641 const KURL& DocumentLoader::requestURL() const
    642 {
    643     return request().url();
    644 }
    645 
    646 const KURL& DocumentLoader::responseURL() const
    647 {
    648     return m_response.url();
    649 }
    650 
    651 const String& DocumentLoader::responseMIMEType() const
    652 {
    653     return m_response.mimeType();
    654 }
    655 
    656 const KURL& DocumentLoader::unreachableURL() const
    657 {
    658     return m_substituteData.failingURL();
    659 }
    660 
    661 void DocumentLoader::setDefersLoading(bool defers)
    662 {
    663     if (m_mainResourceLoader)
    664         m_mainResourceLoader->setDefersLoading(defers);
    665     setAllDefersLoading(m_subresourceLoaders, defers);
    666     setAllDefersLoading(m_plugInStreamLoaders, defers);
    667     if (!defers)
    668         deliverSubstituteResourcesAfterDelay();
    669 }
    670 
    671 void DocumentLoader::stopLoadingPlugIns()
    672 {
    673     cancelAll(m_plugInStreamLoaders);
    674 }
    675 
    676 void DocumentLoader::stopLoadingSubresources()
    677 {
    678     cancelAll(m_subresourceLoaders);
    679 }
    680 
    681 void DocumentLoader::addSubresourceLoader(ResourceLoader* loader)
    682 {
    683     m_subresourceLoaders.add(loader);
    684     setLoading(true);
    685 }
    686 
    687 void DocumentLoader::removeSubresourceLoader(ResourceLoader* loader)
    688 {
    689     m_subresourceLoaders.remove(loader);
    690     updateLoading();
    691     if (Frame* frame = m_frame)
    692         frame->loader()->checkLoadComplete();
    693 }
    694 
    695 void DocumentLoader::addPlugInStreamLoader(ResourceLoader* loader)
    696 {
    697     m_plugInStreamLoaders.add(loader);
    698     setLoading(true);
    699 }
    700 
    701 void DocumentLoader::removePlugInStreamLoader(ResourceLoader* loader)
    702 {
    703     m_plugInStreamLoaders.remove(loader);
    704     updateLoading();
    705 }
    706 
    707 bool DocumentLoader::isLoadingMainResource() const
    708 {
    709     return !!m_mainResourceLoader;
    710 }
    711 
    712 bool DocumentLoader::isLoadingSubresources() const
    713 {
    714     return !m_subresourceLoaders.isEmpty();
    715 }
    716 
    717 bool DocumentLoader::isLoadingPlugIns() const
    718 {
    719     return !m_plugInStreamLoaders.isEmpty();
    720 }
    721 
    722 bool DocumentLoader::isLoadingMultipartContent() const
    723 {
    724     return m_mainResourceLoader && m_mainResourceLoader->isLoadingMultipartContent();
    725 }
    726 
    727 bool DocumentLoader::startLoadingMainResource(unsigned long identifier)
    728 {
    729     ASSERT(!m_mainResourceLoader);
    730     m_mainResourceLoader = MainResourceLoader::create(m_frame);
    731     m_mainResourceLoader->setIdentifier(identifier);
    732 
    733     // FIXME: Is there any way the extra fields could have not been added by now?
    734     // If not, it would be great to remove this line of code.
    735     frameLoader()->addExtraFieldsToMainResourceRequest(m_request);
    736 
    737     if (!m_mainResourceLoader->load(m_request, m_substituteData)) {
    738         // FIXME: If this should really be caught, we should just ASSERT this doesn't happen;
    739         // should it be caught by other parts of WebKit or other parts of the app?
    740         LOG_ERROR("could not create WebResourceHandle for URL %s -- should be caught by policy handler level", m_request.url().string().ascii().data());
    741         m_mainResourceLoader = 0;
    742         return false;
    743     }
    744 
    745     return true;
    746 }
    747 
    748 void DocumentLoader::cancelMainResourceLoad(const ResourceError& error)
    749 {
    750     m_mainResourceLoader->cancel(error);
    751 }
    752 
    753 void DocumentLoader::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader)
    754 {
    755     m_multipartSubresourceLoaders.add(loader);
    756     m_subresourceLoaders.remove(loader);
    757     updateLoading();
    758     if (Frame* frame = m_frame)
    759         frame->loader()->checkLoadComplete();
    760 }
    761 
    762 void DocumentLoader::iconLoadDecisionAvailable()
    763 {
    764     if (m_frame)
    765         m_frame->loader()->iconLoadDecisionAvailable();
    766 }
    767 
    768 }
    769