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