• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
isArchiveMIMEType(const String & mimeType)71 static bool isArchiveMIMEType(const String& mimeType)
72 {
73     return mimeType == "multipart/related";
74 }
75 
DocumentLoader(LocalFrame * frame,const ResourceRequest & req,const SubstituteData & substituteData)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 
frameLoader() const91 FrameLoader* DocumentLoader::frameLoader() const
92 {
93     if (!m_frame)
94         return 0;
95     return &m_frame->loader();
96 }
97 
mainResourceLoader() const98 ResourceLoader* DocumentLoader::mainResourceLoader() const
99 {
100     return m_mainResource ? m_mainResource->loader() : 0;
101 }
102 
~DocumentLoader()103 DocumentLoader::~DocumentLoader()
104 {
105     ASSERT(!m_frame || !isLoading());
106     m_fetcher->clearDocumentLoader();
107     clearMainResourceHandle();
108     m_applicationCacheHost->dispose();
109 }
110 
mainResourceIdentifier() const111 unsigned long DocumentLoader::mainResourceIdentifier() const
112 {
113     return m_mainResource ? m_mainResource->identifier() : 0;
114 }
115 
document() const116 Document* DocumentLoader::document() const
117 {
118     if (m_frame && m_frame->loader().documentLoader() == this)
119         return m_frame->document();
120     return 0;
121 }
122 
originalRequest() const123 const ResourceRequest& DocumentLoader::originalRequest() const
124 {
125     return m_originalRequest;
126 }
127 
request() const128 const ResourceRequest& DocumentLoader::request() const
129 {
130     return m_request;
131 }
132 
url() const133 const KURL& DocumentLoader::url() const
134 {
135     return m_request.url();
136 }
137 
updateForSameDocumentNavigation(const KURL & newURL,SameDocumentNavigationSource sameDocumentNavigationSource)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 
urlForHistory() const153 const KURL& DocumentLoader::urlForHistory() const
154 {
155     return unreachableURL().isEmpty() ? url() : unreachableURL();
156 }
157 
setMainDocumentError(const ResourceError & error)158 void DocumentLoader::setMainDocumentError(const ResourceError& error)
159 {
160     m_mainDocumentError = error;
161 }
162 
mainReceivedError(const ResourceError & error)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.
stopLoading()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 
commitIfReady()220 void DocumentLoader::commitIfReady()
221 {
222     if (!m_committed) {
223         m_committed = true;
224         frameLoader()->commitProvisionalLoad();
225     }
226 }
227 
isLoading() const228 bool DocumentLoader::isLoading() const
229 {
230     if (document() && document()->hasActiveParser())
231         return true;
232 
233     return m_loadingMainResource || m_fetcher->isFetching();
234 }
235 
notifyFinished(Resource * resource)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 
finishedLoading(double finishTime)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 
isRedirectAfterPost(const ResourceRequest & newRequest,const ResourceResponse & redirectResponse)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 
shouldContinueForNavigationPolicy(const ResourceRequest & request,ContentSecurityPolicyCheck shouldCheckMainWorldContentSecurityPolicy,bool isTransitionNavigation)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 
redirectReceived(Resource * resource,ResourceRequest & request,const ResourceResponse & redirectResponse)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 
updateRequest(Resource * resource,const ResourceRequest & request)339 void DocumentLoader::updateRequest(Resource* resource, const ResourceRequest& request)
340 {
341     ASSERT_UNUSED(resource, resource == m_mainResource);
342     m_request = request;
343 }
344 
isFormSubmission(NavigationType type)345 static bool isFormSubmission(NavigationType type)
346 {
347     return type == NavigationTypeFormSubmitted || type == NavigationTypeFormResubmitted;
348 }
349 
willSendRequest(ResourceRequest & newRequest,const ResourceResponse & redirectResponse)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 
canShowMIMEType(const String & mimeType,Page * page)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 
shouldContinueForResponse() const400 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 
cancelLoadAfterXFrameOptionsOrCSPDenied(const ResourceResponse & response)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 
responseReceived(Resource * resource,const ResourceResponse & response)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 
ensureWriter(const AtomicString & mimeType,const KURL & overridingURL)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 
commitData(const char * bytes,size_t length)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 
dataReceived(Resource * resource,const char * data,int length)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 
clearRedirectChain()568 void DocumentLoader::clearRedirectChain()
569 {
570     m_redirectChain.clear();
571 }
572 
appendRedirect(const KURL & url)573 void DocumentLoader::appendRedirect(const KURL& url)
574 {
575     m_redirectChain.append(url);
576 }
577 
detachFromFrame()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 
clearMainResourceLoader()593 void DocumentLoader::clearMainResourceLoader()
594 {
595     m_loadingMainResource = false;
596 }
597 
clearMainResourceHandle()598 void DocumentLoader::clearMainResourceHandle()
599 {
600     if (!m_mainResource)
601         return;
602     m_mainResource->removeClient(this);
603     m_mainResource = 0;
604 }
605 
maybeCreateArchive()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 
addAllArchiveResources(MHTMLArchive * archive)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 
prepareSubframeArchiveLoadIfNeeded()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 
scheduleArchiveLoad(Resource * cachedResource,const ResourceRequest & request)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 
responseMIMEType() const686 const AtomicString& DocumentLoader::responseMIMEType() const
687 {
688     return m_response.mimeType();
689 }
690 
unreachableURL() const691 const KURL& DocumentLoader::unreachableURL() const
692 {
693     return m_substituteData.failingURL();
694 }
695 
setDefersLoading(bool defers)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 
maybeLoadEmpty()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 
startLoadingMainResource()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 
cancelMainResourceLoad(const ResourceError & resourceError)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 
attachThreadedDataReceiver(PassOwnPtr<blink::WebThreadedDataReceiver> threadedDataReceiver)781 void DocumentLoader::attachThreadedDataReceiver(PassOwnPtr<blink::WebThreadedDataReceiver> threadedDataReceiver)
782 {
783     if (mainResourceLoader())
784         mainResourceLoader()->attachThreadedDataReceiver(threadedDataReceiver);
785 }
786 
endWriting(DocumentWriter * writer)787 void DocumentLoader::endWriting(DocumentWriter* writer)
788 {
789     ASSERT_UNUSED(writer, m_writer == writer);
790     m_writer->end();
791     m_writer.clear();
792 }
793 
createWriterFor(const Document * ownerDocument,const DocumentInit & init,const AtomicString & mimeType,const AtomicString & encoding,bool dispatch)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 
mimeType() const817 const AtomicString& DocumentLoader::mimeType() const
818 {
819     if (m_writer)
820         return m_writer->mimeType();
821     return m_response.mimeType();
822 }
823 
setUserChosenEncoding(const String & charset)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()
replaceDocumentWhileExecutingJavaScriptURL(const DocumentInit & init,const String & source,Document * ownerDocument)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