Home | History | Annotate | Download | only in loader
      1 /*
      2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
      3  * Copyright (C) 2011 Google Inc. All rights reserved.
      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 "core/loader/DocumentLoader.h"
     32 
     33 #include "core/FetchInitiatorTypeNames.h"
     34 #include "core/dom/Document.h"
     35 #include "core/dom/DocumentParser.h"
     36 #include "core/events/Event.h"
     37 #include "core/fetch/MemoryCache.h"
     38 #include "core/fetch/ResourceFetcher.h"
     39 #include "core/fetch/ResourceLoader.h"
     40 #include "core/frame/LocalDOMWindow.h"
     41 #include "core/frame/LocalFrame.h"
     42 #include "core/frame/csp/ContentSecurityPolicy.h"
     43 #include "core/html/HTMLFrameOwnerElement.h"
     44 #include "core/html/parser/TextResourceDecoder.h"
     45 #include "core/inspector/InspectorInstrumentation.h"
     46 #include "core/loader/FrameLoader.h"
     47 #include "core/loader/FrameLoaderClient.h"
     48 #include "core/loader/UniqueIdentifier.h"
     49 #include "core/loader/appcache/ApplicationCacheHost.h"
     50 #include "core/page/FrameTree.h"
     51 #include "core/page/Page.h"
     52 #include "core/frame/Settings.h"
     53 #include "core/inspector/ConsoleMessage.h"
     54 #include "platform/Logging.h"
     55 #include "platform/UserGestureIndicator.h"
     56 #include "platform/mhtml/ArchiveResource.h"
     57 #include "platform/mhtml/ArchiveResourceCollection.h"
     58 #include "platform/mhtml/MHTMLArchive.h"
     59 #include "platform/network/ContentSecurityPolicyResponseHeaders.h"
     60 #include "platform/plugins/PluginData.h"
     61 #include "platform/weborigin/SchemeRegistry.h"
     62 #include "platform/weborigin/SecurityPolicy.h"
     63 #include "public/platform/Platform.h"
     64 #include "public/platform/WebMimeRegistry.h"
     65 #include "public/platform/WebThreadedDataReceiver.h"
     66 #include "wtf/Assertions.h"
     67 #include "wtf/text/WTFString.h"
     68 
     69 namespace blink {
     70 
     71 static bool isArchiveMIMEType(const String& mimeType)
     72 {
     73     return mimeType == "multipart/related";
     74 }
     75 
     76 DocumentLoader::DocumentLoader(LocalFrame* frame, const ResourceRequest& req, const SubstituteData& substituteData)
     77     : m_frame(frame)
     78     , m_fetcher(ResourceFetcher::create(this))
     79     , m_originalRequest(req)
     80     , m_substituteData(substituteData)
     81     , m_request(req)
     82     , m_committed(false)
     83     , m_isClientRedirect(false)
     84     , m_replacesCurrentHistoryItem(false)
     85     , m_loadingMainResource(false)
     86     , m_timeOfLastDataReceived(0.0)
     87     , m_applicationCacheHost(ApplicationCacheHost::create(this))
     88 {
     89 }
     90 
     91 FrameLoader* DocumentLoader::frameLoader() const
     92 {
     93     if (!m_frame)
     94         return 0;
     95     return &m_frame->loader();
     96 }
     97 
     98 ResourceLoader* DocumentLoader::mainResourceLoader() const
     99 {
    100     return m_mainResource ? m_mainResource->loader() : 0;
    101 }
    102 
    103 DocumentLoader::~DocumentLoader()
    104 {
    105     ASSERT(!m_frame || !isLoading());
    106     m_fetcher->clearDocumentLoader();
    107     clearMainResourceHandle();
    108     m_applicationCacheHost->dispose();
    109 }
    110 
    111 unsigned long DocumentLoader::mainResourceIdentifier() const
    112 {
    113     return m_mainResource ? m_mainResource->identifier() : 0;
    114 }
    115 
    116 Document* DocumentLoader::document() const
    117 {
    118     if (m_frame && m_frame->loader().documentLoader() == this)
    119         return m_frame->document();
    120     return 0;
    121 }
    122 
    123 const ResourceRequest& DocumentLoader::originalRequest() const
    124 {
    125     return m_originalRequest;
    126 }
    127 
    128 const ResourceRequest& DocumentLoader::request() const
    129 {
    130     return m_request;
    131 }
    132 
    133 const KURL& DocumentLoader::url() const
    134 {
    135     return m_request.url();
    136 }
    137 
    138 void DocumentLoader::updateForSameDocumentNavigation(const KURL& newURL, SameDocumentNavigationSource sameDocumentNavigationSource)
    139 {
    140     KURL oldURL = m_request.url();
    141     m_originalRequest.setURL(newURL);
    142     m_request.setURL(newURL);
    143     if (sameDocumentNavigationSource == SameDocumentNavigationHistoryApi) {
    144         m_request.setHTTPMethod("GET");
    145         m_request.setHTTPBody(nullptr);
    146     }
    147     clearRedirectChain();
    148     if (m_isClientRedirect)
    149         appendRedirect(oldURL);
    150     appendRedirect(newURL);
    151 }
    152 
    153 const KURL& DocumentLoader::urlForHistory() const
    154 {
    155     return unreachableURL().isEmpty() ? url() : unreachableURL();
    156 }
    157 
    158 void DocumentLoader::setMainDocumentError(const ResourceError& error)
    159 {
    160     m_mainDocumentError = error;
    161 }
    162 
    163 void DocumentLoader::mainReceivedError(const ResourceError& error)
    164 {
    165     ASSERT(!error.isNull());
    166     ASSERT(!mainResourceLoader() || !mainResourceLoader()->defersLoading() || InspectorInstrumentation::isDebuggerPaused(m_frame));
    167     m_applicationCacheHost->failedLoadingMainResource();
    168     if (!frameLoader())
    169         return;
    170     setMainDocumentError(error);
    171     clearMainResourceLoader();
    172     frameLoader()->receivedMainResourceError(error);
    173     clearMainResourceHandle();
    174 }
    175 
    176 // Cancels the data source's pending loads.  Conceptually, a data source only loads
    177 // one document at a time, but one document may have many related resources.
    178 // stopLoading will stop all loads initiated by the data source,
    179 // but not loads initiated by child frames' data sources -- that's the WebFrame's job.
    180 void DocumentLoader::stopLoading()
    181 {
    182     RefPtrWillBeRawPtr<LocalFrame> protectFrame(m_frame);
    183     RefPtr<DocumentLoader> protectLoader(this);
    184 
    185     // In some rare cases, calling FrameLoader::stopLoading could cause isLoading() to return false.
    186     // (This can happen when there's a single XMLHttpRequest currently loading and stopLoading causes it
    187     // to stop loading. Because of this, we need to save it so we don't return early.
    188     bool loading = isLoading();
    189 
    190     if (m_committed) {
    191         // Attempt to stop the frame if the document loader is loading, or if it is done loading but
    192         // still  parsing. Failure to do so can cause a world leak.
    193         Document* doc = m_frame->document();
    194 
    195         if (loading || doc->parsing())
    196             m_frame->loader().stopLoading();
    197     }
    198 
    199     if (!loading) {
    200         m_fetcher->stopFetching();
    201         return;
    202     }
    203 
    204     if (m_loadingMainResource) {
    205         // Stop the main resource loader and let it send the cancelled message.
    206         cancelMainResourceLoad(ResourceError::cancelledError(m_request.url()));
    207     } else if (m_fetcher->isFetching()) {
    208         // The main resource loader already finished loading. Set the cancelled error on the
    209         // document and let the resourceLoaders send individual cancelled messages below.
    210         setMainDocumentError(ResourceError::cancelledError(m_request.url()));
    211     } else {
    212         // If there are no resource loaders, we need to manufacture a cancelled message.
    213         // (A back/forward navigation has no resource loaders because its resources are cached.)
    214         mainReceivedError(ResourceError::cancelledError(m_request.url()));
    215     }
    216 
    217     m_fetcher->stopFetching();
    218 }
    219 
    220 void DocumentLoader::commitIfReady()
    221 {
    222     if (!m_committed) {
    223         m_committed = true;
    224         frameLoader()->commitProvisionalLoad();
    225     }
    226 }
    227 
    228 bool DocumentLoader::isLoading() const
    229 {
    230     if (document() && document()->hasActiveParser())
    231         return true;
    232 
    233     return m_loadingMainResource || m_fetcher->isFetching();
    234 }
    235 
    236 void DocumentLoader::notifyFinished(Resource* resource)
    237 {
    238     ASSERT_UNUSED(resource, m_mainResource == resource);
    239     ASSERT(m_mainResource);
    240 
    241     RefPtr<DocumentLoader> protect(this);
    242 
    243     if (!m_mainResource->errorOccurred() && !m_mainResource->wasCanceled()) {
    244         finishedLoading(m_mainResource->loadFinishTime());
    245         return;
    246     }
    247 
    248     mainReceivedError(m_mainResource->resourceError());
    249 }
    250 
    251 void DocumentLoader::finishedLoading(double finishTime)
    252 {
    253     ASSERT(!mainResourceLoader() || !mainResourceLoader()->defersLoading() || InspectorInstrumentation::isDebuggerPaused(m_frame));
    254 
    255     RefPtr<DocumentLoader> protect(this);
    256 
    257     double responseEndTime = finishTime;
    258     if (!responseEndTime)
    259         responseEndTime = m_timeOfLastDataReceived;
    260     if (!responseEndTime)
    261         responseEndTime = monotonicallyIncreasingTime();
    262     timing()->setResponseEnd(responseEndTime);
    263 
    264     commitIfReady();
    265     if (!frameLoader())
    266         return;
    267 
    268     if (!maybeCreateArchive()) {
    269         // If this is an empty document, it will not have actually been created yet. Commit dummy data so that
    270         // DocumentWriter::begin() gets called and creates the Document.
    271         if (!m_writer)
    272             commitData(0, 0);
    273     }
    274 
    275     endWriting(m_writer.get());
    276 
    277     if (!m_mainDocumentError.isNull())
    278         return;
    279     clearMainResourceLoader();
    280     if (!frameLoader()->stateMachine()->creatingInitialEmptyDocument())
    281         frameLoader()->checkLoadComplete();
    282 
    283     // If the document specified an application cache manifest, it violates the author's intent if we store it in the memory cache
    284     // and deny the appcache the chance to intercept it in the future, so remove from the memory cache.
    285     if (m_frame) {
    286         if (m_mainResource && m_frame->document()->hasAppCacheManifest())
    287             memoryCache()->remove(m_mainResource.get());
    288     }
    289     m_applicationCacheHost->finishedLoadingMainResource();
    290     clearMainResourceHandle();
    291 }
    292 
    293 bool DocumentLoader::isRedirectAfterPost(const ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
    294 {
    295     int status = redirectResponse.httpStatusCode();
    296     if (((status >= 301 && status <= 303) || status == 307)
    297         && m_originalRequest.httpMethod() == "POST")
    298         return true;
    299 
    300     return false;
    301 }
    302 
    303 bool DocumentLoader::shouldContinueForNavigationPolicy(const ResourceRequest& request, ContentSecurityPolicyCheck shouldCheckMainWorldContentSecurityPolicy, bool isTransitionNavigation)
    304 {
    305     // Don't ask if we are loading an empty URL.
    306     if (request.url().isEmpty() || m_substituteData.isValid())
    307         return true;
    308 
    309     // If we're loading content into a subframe, check against the parent's Content Security Policy
    310     // and kill the load if that check fails, unless we should bypass the main world's CSP.
    311     // FIXME: CSP checks are broken for OOPI. For now, this policy always allows frames with a remote parent...
    312     if ((shouldCheckMainWorldContentSecurityPolicy == CheckContentSecurityPolicy) && (m_frame->deprecatedLocalOwner() && !m_frame->deprecatedLocalOwner()->document().contentSecurityPolicy()->allowChildFrameFromSource(request.url()))) {
    313         // Fire a load event, as timing attacks would otherwise reveal that the
    314         // frame was blocked. This way, it looks like every other cross-origin
    315         // page load.
    316         m_frame->document()->enforceSandboxFlags(SandboxOrigin);
    317         m_frame->owner()->dispatchLoad();
    318         return false;
    319     }
    320 
    321     NavigationPolicy policy = m_triggeringAction.policy();
    322     policy = frameLoader()->client()->decidePolicyForNavigation(request, this, policy, isTransitionNavigation);
    323     if (policy == NavigationPolicyCurrentTab)
    324         return true;
    325     if (policy == NavigationPolicyIgnore)
    326         return false;
    327     if (!LocalDOMWindow::allowPopUp(*m_frame) && !UserGestureIndicator::processingUserGesture())
    328         return false;
    329     frameLoader()->client()->loadURLExternally(request, policy);
    330     return false;
    331 }
    332 
    333 void DocumentLoader::redirectReceived(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
    334 {
    335     ASSERT_UNUSED(resource, resource == m_mainResource);
    336     willSendRequest(request, redirectResponse);
    337 }
    338 
    339 void DocumentLoader::updateRequest(Resource* resource, const ResourceRequest& request)
    340 {
    341     ASSERT_UNUSED(resource, resource == m_mainResource);
    342     m_request = request;
    343 }
    344 
    345 static bool isFormSubmission(NavigationType type)
    346 {
    347     return type == NavigationTypeFormSubmitted || type == NavigationTypeFormResubmitted;
    348 }
    349 
    350 void DocumentLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
    351 {
    352     // Note that there are no asserts here as there are for the other callbacks. This is due to the
    353     // fact that this "callback" is sent when starting every load, and the state of callback
    354     // deferrals plays less of a part in this function in preventing the bad behavior deferring
    355     // callbacks is meant to prevent.
    356     ASSERT(!newRequest.isNull());
    357     if (isFormSubmission(m_triggeringAction.type()) && !m_frame->document()->contentSecurityPolicy()->allowFormAction(newRequest.url())) {
    358         cancelMainResourceLoad(ResourceError::cancelledError(newRequest.url()));
    359         return;
    360     }
    361 
    362     ASSERT(timing()->fetchStart());
    363     if (!redirectResponse.isNull()) {
    364         // If the redirecting url is not allowed to display content from the target origin,
    365         // then block the redirect.
    366         RefPtr<SecurityOrigin> redirectingOrigin = SecurityOrigin::create(redirectResponse.url());
    367         if (!redirectingOrigin->canDisplay(newRequest.url())) {
    368             FrameLoader::reportLocalLoadFailed(m_frame, newRequest.url().string());
    369             cancelMainResourceLoad(ResourceError::cancelledError(newRequest.url()));
    370             return;
    371         }
    372         timing()->addRedirect(redirectResponse.url(), newRequest.url());
    373     }
    374 
    375     // If we're fielding a redirect in response to a POST, force a load from origin, since
    376     // this is a common site technique to return to a page viewing some data that the POST
    377     // just modified.
    378     if (newRequest.cachePolicy() == UseProtocolCachePolicy && isRedirectAfterPost(newRequest, redirectResponse))
    379         newRequest.setCachePolicy(ReloadBypassingCache);
    380 
    381     m_request = newRequest;
    382 
    383     if (redirectResponse.isNull())
    384         return;
    385 
    386     appendRedirect(newRequest.url());
    387     frameLoader()->client()->dispatchDidReceiveServerRedirectForProvisionalLoad();
    388     if (!shouldContinueForNavigationPolicy(newRequest, CheckContentSecurityPolicy))
    389         cancelMainResourceLoad(ResourceError::cancelledError(m_request.url()));
    390 }
    391 
    392 static bool canShowMIMEType(const String& mimeType, Page* page)
    393 {
    394     if (blink::Platform::current()->mimeRegistry()->supportsMIMEType(mimeType) == blink::WebMimeRegistry::IsSupported)
    395         return true;
    396     PluginData* pluginData = page->pluginData();
    397     return !mimeType.isEmpty() && pluginData && pluginData->supportsMimeType(mimeType);
    398 }
    399 
    400 bool DocumentLoader::shouldContinueForResponse() const
    401 {
    402     if (m_substituteData.isValid())
    403         return true;
    404 
    405     int statusCode = m_response.httpStatusCode();
    406     if (statusCode == 204 || statusCode == 205) {
    407         // The server does not want us to replace the page contents.
    408         return false;
    409     }
    410 
    411     if (contentDispositionType(m_response.httpHeaderField("Content-Disposition")) == ContentDispositionAttachment) {
    412         // The server wants us to download instead of replacing the page contents.
    413         // Downloading is handled by the embedder, but we still get the initial
    414         // response so that we can ignore it and clean up properly.
    415         return false;
    416     }
    417 
    418     if (!canShowMIMEType(m_response.mimeType(), m_frame->page()))
    419         return false;
    420 
    421     // Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks.
    422     if (equalIgnoringCase("multipart/related", m_response.mimeType()) && !SchemeRegistry::shouldTreatURLSchemeAsLocal(m_request.url().protocol()))
    423         return false;
    424 
    425     return true;
    426 }
    427 
    428 void DocumentLoader::cancelLoadAfterXFrameOptionsOrCSPDenied(const ResourceResponse& response)
    429 {
    430     InspectorInstrumentation::continueAfterXFrameOptionsDenied(m_frame, this, mainResourceIdentifier(), response);
    431 
    432     frame()->document()->enforceSandboxFlags(SandboxOrigin);
    433     if (FrameOwner* owner = frame()->owner())
    434         owner->dispatchLoad();
    435 
    436     // The load event might have detached this frame. In that case, the load will already have been cancelled during detach.
    437     if (frameLoader())
    438         cancelMainResourceLoad(ResourceError::cancelledError(m_request.url()));
    439     return;
    440 }
    441 
    442 void DocumentLoader::responseReceived(Resource* resource, const ResourceResponse& response)
    443 {
    444     ASSERT_UNUSED(resource, m_mainResource == resource);
    445     RefPtr<DocumentLoader> protect(this);
    446 
    447     m_applicationCacheHost->didReceiveResponseForMainResource(response);
    448 
    449     // The memory cache doesn't understand the application cache or its caching rules. So if a main resource is served
    450     // from the application cache, ensure we don't save the result for future use. All responses loaded
    451     // from appcache will have a non-zero appCacheID().
    452     if (response.appCacheID())
    453         memoryCache()->remove(m_mainResource.get());
    454 
    455     DEFINE_STATIC_LOCAL(AtomicString, xFrameOptionHeader, ("x-frame-options", AtomicString::ConstructFromLiteral));
    456     HTTPHeaderMap::const_iterator it = response.httpHeaderFields().find(xFrameOptionHeader);
    457     if (it != response.httpHeaderFields().end()) {
    458         String content = it->value;
    459         if (frameLoader()->shouldInterruptLoadForXFrameOptions(content, response.url(), mainResourceIdentifier())) {
    460             String message = "Refused to display '" + response.url().elidedString() + "' in a frame because it set 'X-Frame-Options' to '" + content + "'.";
    461             RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = ConsoleMessage::create(SecurityMessageSource, ErrorMessageLevel, message);
    462             consoleMessage->setRequestIdentifier(mainResourceIdentifier());
    463             frame()->document()->addConsoleMessage(consoleMessage.release());
    464 
    465             cancelLoadAfterXFrameOptionsOrCSPDenied(response);
    466             return;
    467         }
    468     }
    469 
    470     m_contentSecurityPolicy = ContentSecurityPolicy::create();
    471     m_contentSecurityPolicy->setOverrideURLForSelf(response.url());
    472     m_contentSecurityPolicy->didReceiveHeaders(ContentSecurityPolicyResponseHeaders(response));
    473     if (!m_contentSecurityPolicy->allowAncestors(m_frame, response.url())) {
    474         cancelLoadAfterXFrameOptionsOrCSPDenied(response);
    475         return;
    476     }
    477 
    478     ASSERT(!mainResourceLoader() || !mainResourceLoader()->defersLoading());
    479 
    480     m_response = response;
    481 
    482     if (isArchiveMIMEType(m_response.mimeType()) && m_mainResource->dataBufferingPolicy() != BufferData)
    483         m_mainResource->setDataBufferingPolicy(BufferData);
    484 
    485     if (!shouldContinueForResponse()) {
    486         InspectorInstrumentation::continueWithPolicyIgnore(m_frame, this, m_mainResource->identifier(), m_response);
    487         cancelMainResourceLoad(ResourceError::cancelledError(m_request.url()));
    488         return;
    489     }
    490 
    491     if (m_response.isHTTP()) {
    492         int status = m_response.httpStatusCode();
    493         // FIXME: Fallback content only works if the parent is in the same processs.
    494         if ((status < 200 || status >= 300) && m_frame->owner()) {
    495             if (!m_frame->deprecatedLocalOwner()) {
    496                 ASSERT_NOT_REACHED();
    497             } else if (m_frame->deprecatedLocalOwner()->isObjectElement()) {
    498                 m_frame->deprecatedLocalOwner()->renderFallbackContent();
    499                 // object elements are no longer rendered after we fallback, so don't
    500                 // keep trying to process data from their load
    501                 cancelMainResourceLoad(ResourceError::cancelledError(m_request.url()));
    502             }
    503         }
    504     }
    505 }
    506 
    507 void DocumentLoader::ensureWriter(const AtomicString& mimeType, const KURL& overridingURL)
    508 {
    509     if (m_writer)
    510         return;
    511 
    512     const AtomicString& encoding = overrideEncoding().isNull() ? response().textEncodingName() : overrideEncoding();
    513 
    514     // Prepare a DocumentInit before clearing the frame, because it may need to
    515     // inherit an aliased security context.
    516     DocumentInit init(url(), m_frame);
    517     init.withNewRegistrationContext();
    518     m_frame->loader().clear();
    519     ASSERT(m_frame->page());
    520 
    521     m_writer = createWriterFor(0, init, mimeType, encoding, false);
    522     m_writer->setDocumentWasLoadedAsPartOfNavigation();
    523     // This should be set before receivedFirstData().
    524     if (!overridingURL.isEmpty())
    525         m_frame->document()->setBaseURLOverride(overridingURL);
    526 
    527     // Call receivedFirstData() exactly once per load.
    528     frameLoader()->receivedFirstData();
    529     m_frame->document()->maybeHandleHttpRefresh(m_response.httpHeaderField("Refresh"), Document::HttpRefreshFromHeader);
    530 }
    531 
    532 void DocumentLoader::commitData(const char* bytes, size_t length)
    533 {
    534     ensureWriter(m_response.mimeType());
    535     ASSERT(m_frame->document()->parsing());
    536     m_writer->addData(bytes, length);
    537 }
    538 
    539 void DocumentLoader::dataReceived(Resource* resource, const char* data, int length)
    540 {
    541     ASSERT(data);
    542     ASSERT(length);
    543     ASSERT_UNUSED(resource, resource == m_mainResource);
    544     ASSERT(!m_response.isNull());
    545     ASSERT(!mainResourceLoader() || !mainResourceLoader()->defersLoading());
    546 
    547     // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource
    548     // by starting a new load, so retain temporarily.
    549     RefPtrWillBeRawPtr<LocalFrame> protectFrame(m_frame);
    550     RefPtr<DocumentLoader> protectLoader(this);
    551 
    552     m_applicationCacheHost->mainResourceDataReceived(data, length);
    553     m_timeOfLastDataReceived = monotonicallyIncreasingTime();
    554 
    555     commitIfReady();
    556     if (!frameLoader())
    557         return;
    558     if (isArchiveMIMEType(response().mimeType()))
    559         return;
    560     commitData(data, length);
    561 
    562     // If we are sending data to MediaDocument, we should stop here
    563     // and cancel the request.
    564     if (m_frame && m_frame->document()->isMediaDocument())
    565         cancelMainResourceLoad(ResourceError::cancelledError(m_request.url()));
    566 }
    567 
    568 void DocumentLoader::clearRedirectChain()
    569 {
    570     m_redirectChain.clear();
    571 }
    572 
    573 void DocumentLoader::appendRedirect(const KURL& url)
    574 {
    575     m_redirectChain.append(url);
    576 }
    577 
    578 void DocumentLoader::detachFromFrame()
    579 {
    580     ASSERT(m_frame);
    581     RefPtrWillBeRawPtr<LocalFrame> protectFrame(m_frame);
    582     RefPtr<DocumentLoader> protectLoader(this);
    583 
    584     // It never makes sense to have a document loader that is detached from its
    585     // frame have any loads active, so go ahead and kill all the loads.
    586     stopLoading();
    587 
    588     m_applicationCacheHost->setApplicationCache(0);
    589     InspectorInstrumentation::loaderDetachedFromFrame(m_frame, this);
    590     m_frame = 0;
    591 }
    592 
    593 void DocumentLoader::clearMainResourceLoader()
    594 {
    595     m_loadingMainResource = false;
    596 }
    597 
    598 void DocumentLoader::clearMainResourceHandle()
    599 {
    600     if (!m_mainResource)
    601         return;
    602     m_mainResource->removeClient(this);
    603     m_mainResource = 0;
    604 }
    605 
    606 bool DocumentLoader::maybeCreateArchive()
    607 {
    608     // Only the top-frame can load MHTML.
    609     if (m_frame->tree().parent())
    610         return false;
    611 
    612     // Give the archive machinery a crack at this document. If the MIME type is not an archive type, it will return 0.
    613     if (!isArchiveMIMEType(m_response.mimeType()))
    614         return false;
    615 
    616     ASSERT(m_mainResource);
    617     m_archive = MHTMLArchive::create(m_response.url(), m_mainResource->resourceBuffer());
    618     // Invalid MHTML.
    619     if (!m_archive || !m_archive->mainResource()) {
    620         m_archive.clear();
    621         return false;
    622     }
    623 
    624     addAllArchiveResources(m_archive.get());
    625     ArchiveResource* mainResource = m_archive->mainResource();
    626 
    627     // The origin is the MHTML file, we need to set the base URL to the document encoded in the MHTML so
    628     // relative URLs are resolved properly.
    629     ensureWriter(mainResource->mimeType(), m_archive->mainResource()->url());
    630 
    631     // The Document has now been created.
    632     document()->enforceSandboxFlags(SandboxAll);
    633 
    634     commitData(mainResource->data()->data(), mainResource->data()->size());
    635     return true;
    636 }
    637 
    638 void DocumentLoader::addAllArchiveResources(MHTMLArchive* archive)
    639 {
    640     ASSERT(archive);
    641     if (!m_archiveResourceCollection)
    642         m_archiveResourceCollection = ArchiveResourceCollection::create();
    643     m_archiveResourceCollection->addAllResources(archive);
    644 }
    645 
    646 void DocumentLoader::prepareSubframeArchiveLoadIfNeeded()
    647 {
    648     if (!m_frame->tree().parent() || !m_frame->tree().parent()->isLocalFrame())
    649         return;
    650 
    651     ArchiveResourceCollection* parentCollection = toLocalFrame(m_frame->tree().parent())->loader().documentLoader()->m_archiveResourceCollection.get();
    652     if (!parentCollection)
    653         return;
    654 
    655     m_archive = parentCollection->popSubframeArchive(m_frame->tree().uniqueName(), m_request.url());
    656 
    657     if (!m_archive)
    658         return;
    659     addAllArchiveResources(m_archive.get());
    660 
    661     ArchiveResource* mainResource = m_archive->mainResource();
    662     m_substituteData = SubstituteData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), KURL());
    663 }
    664 
    665 bool DocumentLoader::scheduleArchiveLoad(Resource* cachedResource, const ResourceRequest& request)
    666 {
    667     if (!m_archive)
    668         return false;
    669 
    670     ASSERT(m_archiveResourceCollection);
    671     ArchiveResource* archiveResource = m_archiveResourceCollection->archiveResourceForURL(request.url());
    672     if (!archiveResource) {
    673         cachedResource->error(Resource::LoadError);
    674         return true;
    675     }
    676 
    677     cachedResource->setLoading(true);
    678     cachedResource->responseReceived(archiveResource->response());
    679     SharedBuffer* data = archiveResource->data();
    680     if (data)
    681         cachedResource->appendData(data->data(), data->size());
    682     cachedResource->finish();
    683     return true;
    684 }
    685 
    686 const AtomicString& DocumentLoader::responseMIMEType() const
    687 {
    688     return m_response.mimeType();
    689 }
    690 
    691 const KURL& DocumentLoader::unreachableURL() const
    692 {
    693     return m_substituteData.failingURL();
    694 }
    695 
    696 void DocumentLoader::setDefersLoading(bool defers)
    697 {
    698     // Multiple frames may be loading the same main resource simultaneously. If deferral state changes,
    699     // each frame's DocumentLoader will try to send a setDefersLoading() to the same underlying ResourceLoader. Ensure only
    700     // the "owning" DocumentLoader does so, as setDefersLoading() is not resilient to setting the same value repeatedly.
    701     if (mainResourceLoader() && mainResourceLoader()->isLoadedBy(m_fetcher.get()))
    702         mainResourceLoader()->setDefersLoading(defers);
    703 
    704     m_fetcher->setDefersLoading(defers);
    705 }
    706 
    707 bool DocumentLoader::maybeLoadEmpty()
    708 {
    709     bool shouldLoadEmpty = !m_substituteData.isValid() && (m_request.url().isEmpty() || SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(m_request.url().protocol()));
    710     if (!shouldLoadEmpty)
    711         return false;
    712 
    713     if (m_request.url().isEmpty() && !frameLoader()->stateMachine()->creatingInitialEmptyDocument())
    714         m_request.setURL(blankURL());
    715     m_response = ResourceResponse(m_request.url(), "text/html", 0, nullAtom, String());
    716     finishedLoading(monotonicallyIncreasingTime());
    717     return true;
    718 }
    719 
    720 void DocumentLoader::startLoadingMainResource()
    721 {
    722     RefPtr<DocumentLoader> protect(this);
    723     m_mainDocumentError = ResourceError();
    724     timing()->markNavigationStart();
    725     ASSERT(!m_mainResource);
    726     ASSERT(!m_loadingMainResource);
    727     m_loadingMainResource = true;
    728 
    729     if (maybeLoadEmpty())
    730         return;
    731 
    732     ASSERT(timing()->navigationStart());
    733     ASSERT(!timing()->fetchStart());
    734     timing()->markFetchStart();
    735     willSendRequest(m_request, ResourceResponse());
    736 
    737     // willSendRequest() may lead to our LocalFrame being detached or cancelling the load via nulling the ResourceRequest.
    738     if (!m_frame || m_request.isNull())
    739         return;
    740 
    741     m_applicationCacheHost->willStartLoadingMainResource(m_request);
    742     prepareSubframeArchiveLoadIfNeeded();
    743 
    744     ResourceRequest request(m_request);
    745     DEFINE_STATIC_LOCAL(ResourceLoaderOptions, mainResourceLoadOptions,
    746         (DoNotBufferData, AllowStoredCredentials, ClientRequestedCredentials, CheckContentSecurityPolicy, DocumentContext));
    747     FetchRequest cachedResourceRequest(request, FetchInitiatorTypeNames::document, mainResourceLoadOptions);
    748     m_mainResource = m_fetcher->fetchMainResource(cachedResourceRequest, m_substituteData);
    749     if (!m_mainResource) {
    750         m_request = ResourceRequest();
    751         // If the load was aborted by clearing m_request, it's possible the ApplicationCacheHost
    752         // is now in a state where starting an empty load will be inconsistent. Replace it with
    753         // a new ApplicationCacheHost.
    754         m_applicationCacheHost = ApplicationCacheHost::create(this);
    755         maybeLoadEmpty();
    756         return;
    757     }
    758     m_mainResource->addClient(this);
    759 
    760     // A bunch of headers are set when the underlying ResourceLoader is created, and m_request needs to include those.
    761     if (mainResourceLoader())
    762         request = mainResourceLoader()->originalRequest();
    763     // If there was a fragment identifier on m_request, the cache will have stripped it. m_request should include
    764     // the fragment identifier, so add that back in.
    765     if (equalIgnoringFragmentIdentifier(m_request.url(), request.url()))
    766         request.setURL(m_request.url());
    767     m_request = request;
    768 }
    769 
    770 void DocumentLoader::cancelMainResourceLoad(const ResourceError& resourceError)
    771 {
    772     RefPtr<DocumentLoader> protect(this);
    773     ResourceError error = resourceError.isNull() ? ResourceError::cancelledError(m_request.url()) : resourceError;
    774 
    775     if (mainResourceLoader())
    776         mainResourceLoader()->cancel(error);
    777 
    778     mainReceivedError(error);
    779 }
    780 
    781 void DocumentLoader::attachThreadedDataReceiver(PassOwnPtr<blink::WebThreadedDataReceiver> threadedDataReceiver)
    782 {
    783     if (mainResourceLoader())
    784         mainResourceLoader()->attachThreadedDataReceiver(threadedDataReceiver);
    785 }
    786 
    787 void DocumentLoader::endWriting(DocumentWriter* writer)
    788 {
    789     ASSERT_UNUSED(writer, m_writer == writer);
    790     m_writer->end();
    791     m_writer.clear();
    792 }
    793 
    794 PassRefPtrWillBeRawPtr<DocumentWriter> DocumentLoader::createWriterFor(const Document* ownerDocument, const DocumentInit& init, const AtomicString& mimeType, const AtomicString& encoding, bool dispatch)
    795 {
    796     LocalFrame* frame = init.frame();
    797 
    798     if (frame->document())
    799         frame->document()->prepareForDestruction();
    800 
    801     if (!init.shouldReuseDefaultView())
    802         frame->setDOMWindow(LocalDOMWindow::create(*frame));
    803 
    804     RefPtrWillBeRawPtr<Document> document = frame->domWindow()->installNewDocument(mimeType, init);
    805     if (ownerDocument) {
    806         document->setCookieURL(ownerDocument->cookieURL());
    807         document->setSecurityOrigin(ownerDocument->securityOrigin());
    808         if (ownerDocument->isTransitionDocument())
    809             document->setIsTransitionDocument();
    810     }
    811 
    812     frame->loader().didBeginDocument(dispatch);
    813 
    814     return DocumentWriter::create(document.get(), mimeType, encoding);
    815 }
    816 
    817 const AtomicString& DocumentLoader::mimeType() const
    818 {
    819     if (m_writer)
    820         return m_writer->mimeType();
    821     return m_response.mimeType();
    822 }
    823 
    824 void DocumentLoader::setUserChosenEncoding(const String& charset)
    825 {
    826     if (m_writer)
    827         m_writer->setUserChosenEncoding(charset);
    828 }
    829 
    830 // This is only called by FrameLoader::replaceDocumentWhileExecutingJavaScriptURL()
    831 void DocumentLoader::replaceDocumentWhileExecutingJavaScriptURL(const DocumentInit& init, const String& source, Document* ownerDocument)
    832 {
    833     m_writer = createWriterFor(ownerDocument, init, mimeType(), m_writer ? m_writer->encoding() : emptyAtom, true);
    834     if (!source.isNull())
    835         m_writer->appendReplacingData(source);
    836     endWriting(m_writer.get());
    837 }
    838 
    839 } // namespace blink
    840