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
69namespace blink {
70
71static bool isArchiveMIMEType(const String& mimeType)
72{
73    return mimeType == "multipart/related";
74}
75
76DocumentLoader::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
91FrameLoader* DocumentLoader::frameLoader() const
92{
93    if (!m_frame)
94        return 0;
95    return &m_frame->loader();
96}
97
98ResourceLoader* DocumentLoader::mainResourceLoader() const
99{
100    return m_mainResource ? m_mainResource->loader() : 0;
101}
102
103DocumentLoader::~DocumentLoader()
104{
105    ASSERT(!m_frame || !isLoading());
106    m_fetcher->clearDocumentLoader();
107    clearMainResourceHandle();
108    m_applicationCacheHost->dispose();
109}
110
111unsigned long DocumentLoader::mainResourceIdentifier() const
112{
113    return m_mainResource ? m_mainResource->identifier() : 0;
114}
115
116Document* DocumentLoader::document() const
117{
118    if (m_frame && m_frame->loader().documentLoader() == this)
119        return m_frame->document();
120    return 0;
121}
122
123const ResourceRequest& DocumentLoader::originalRequest() const
124{
125    return m_originalRequest;
126}
127
128const ResourceRequest& DocumentLoader::request() const
129{
130    return m_request;
131}
132
133const KURL& DocumentLoader::url() const
134{
135    return m_request.url();
136}
137
138void 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
153const KURL& DocumentLoader::urlForHistory() const
154{
155    return unreachableURL().isEmpty() ? url() : unreachableURL();
156}
157
158void DocumentLoader::setMainDocumentError(const ResourceError& error)
159{
160    m_mainDocumentError = error;
161}
162
163void 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.
180void 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
220void DocumentLoader::commitIfReady()
221{
222    if (!m_committed) {
223        m_committed = true;
224        frameLoader()->commitProvisionalLoad();
225    }
226}
227
228bool DocumentLoader::isLoading() const
229{
230    if (document() && document()->hasActiveParser())
231        return true;
232
233    return m_loadingMainResource || m_fetcher->isFetching();
234}
235
236void 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
251void 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
293bool 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
303bool 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
333void DocumentLoader::redirectReceived(Resource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
334{
335    ASSERT_UNUSED(resource, resource == m_mainResource);
336    willSendRequest(request, redirectResponse);
337}
338
339void DocumentLoader::updateRequest(Resource* resource, const ResourceRequest& request)
340{
341    ASSERT_UNUSED(resource, resource == m_mainResource);
342    m_request = request;
343}
344
345static bool isFormSubmission(NavigationType type)
346{
347    return type == NavigationTypeFormSubmitted || type == NavigationTypeFormResubmitted;
348}
349
350void 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
392static 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
400bool 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
428void 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
442void 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
507void 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
532void 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
539void 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
568void DocumentLoader::clearRedirectChain()
569{
570    m_redirectChain.clear();
571}
572
573void DocumentLoader::appendRedirect(const KURL& url)
574{
575    m_redirectChain.append(url);
576}
577
578void 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
593void DocumentLoader::clearMainResourceLoader()
594{
595    m_loadingMainResource = false;
596}
597
598void DocumentLoader::clearMainResourceHandle()
599{
600    if (!m_mainResource)
601        return;
602    m_mainResource->removeClient(this);
603    m_mainResource = 0;
604}
605
606bool 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
638void DocumentLoader::addAllArchiveResources(MHTMLArchive* archive)
639{
640    ASSERT(archive);
641    if (!m_archiveResourceCollection)
642        m_archiveResourceCollection = ArchiveResourceCollection::create();
643    m_archiveResourceCollection->addAllResources(archive);
644}
645
646void 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
665bool 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
686const AtomicString& DocumentLoader::responseMIMEType() const
687{
688    return m_response.mimeType();
689}
690
691const KURL& DocumentLoader::unreachableURL() const
692{
693    return m_substituteData.failingURL();
694}
695
696void 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
707bool 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
720void 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
770void 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
781void DocumentLoader::attachThreadedDataReceiver(PassOwnPtr<blink::WebThreadedDataReceiver> threadedDataReceiver)
782{
783    if (mainResourceLoader())
784        mainResourceLoader()->attachThreadedDataReceiver(threadedDataReceiver);
785}
786
787void DocumentLoader::endWriting(DocumentWriter* writer)
788{
789    ASSERT_UNUSED(writer, m_writer == writer);
790    m_writer->end();
791    m_writer.clear();
792}
793
794PassRefPtrWillBeRawPtr<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
817const AtomicString& DocumentLoader::mimeType() const
818{
819    if (m_writer)
820        return m_writer->mimeType();
821    return m_response.mimeType();
822}
823
824void 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()
831void 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