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