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 68 static bool isArchiveMIMEType(const String& mimeType) 69 { 70 return mimeType == "multipart/related"; 71 } 72 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 88 FrameLoader* DocumentLoader::frameLoader() const 89 { 90 if (!m_frame) 91 return 0; 92 return &m_frame->loader(); 93 } 94 95 ResourceLoader* DocumentLoader::mainResourceLoader() const 96 { 97 return m_mainResource ? m_mainResource->loader() : 0; 98 } 99 100 DocumentLoader::~DocumentLoader() 101 { 102 ASSERT(!m_frame || !isLoading()); 103 m_fetcher->clearDocumentLoader(); 104 clearMainResourceHandle(); 105 } 106 107 unsigned long DocumentLoader::mainResourceIdentifier() const 108 { 109 return m_mainResource ? m_mainResource->identifier() : 0; 110 } 111 112 Document* DocumentLoader::document() const 113 { 114 if (m_frame && m_frame->loader().documentLoader() == this) 115 return m_frame->document(); 116 return 0; 117 } 118 119 const ResourceRequest& DocumentLoader::originalRequest() const 120 { 121 return m_originalRequest; 122 } 123 124 const ResourceRequest& DocumentLoader::request() const 125 { 126 return m_request; 127 } 128 129 const KURL& DocumentLoader::url() const 130 { 131 return m_request.url(); 132 } 133 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 149 const KURL& DocumentLoader::urlForHistory() const 150 { 151 return unreachableURL().isEmpty() ? url() : unreachableURL(); 152 } 153 154 void DocumentLoader::setMainDocumentError(const ResourceError& error) 155 { 156 m_mainDocumentError = error; 157 } 158 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. 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 216 void DocumentLoader::commitIfReady() 217 { 218 if (!m_committed) { 219 m_committed = true; 220 frameLoader()->commitProvisionalLoad(); 221 } 222 } 223 224 bool DocumentLoader::isLoading() const 225 { 226 if (document() && document()->hasActiveParser()) 227 return true; 228 229 return m_loadingMainResource || m_fetcher->isFetching(); 230 } 231 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 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 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 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 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 335 void DocumentLoader::updateRequest(Resource* resource, const ResourceRequest& request) 336 { 337 ASSERT_UNUSED(resource, resource == m_mainResource); 338 m_request = request; 339 } 340 341 static bool isFormSubmission(NavigationType type) 342 { 343 return type == NavigationTypeFormSubmitted || type == NavigationTypeFormResubmitted; 344 } 345 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 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 406 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 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 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 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 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 551 void DocumentLoader::clearRedirectChain() 552 { 553 m_redirectChain.clear(); 554 } 555 556 void DocumentLoader::appendRedirect(const KURL& url) 557 { 558 m_redirectChain.append(url); 559 } 560 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 576 void DocumentLoader::clearMainResourceLoader() 577 { 578 m_loadingMainResource = false; 579 } 580 581 void DocumentLoader::clearMainResourceHandle() 582 { 583 if (!m_mainResource) 584 return; 585 m_mainResource->removeClient(this); 586 m_mainResource = 0; 587 } 588 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 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 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 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 669 const AtomicString& DocumentLoader::responseMIMEType() const 670 { 671 return m_response.mimeType(); 672 } 673 674 const KURL& DocumentLoader::unreachableURL() const 675 { 676 return m_substituteData.failingURL(); 677 } 678 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 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 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 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 764 void DocumentLoader::attachThreadedDataReceiver(PassOwnPtr<blink::WebThreadedDataReceiver> threadedDataReceiver) 765 { 766 if (mainResourceLoader()) 767 mainResourceLoader()->attachThreadedDataReceiver(threadedDataReceiver); 768 } 769 770 void DocumentLoader::endWriting(DocumentWriter* writer) 771 { 772 ASSERT_UNUSED(writer, m_writer == writer); 773 m_writer->end(); 774 m_writer.clear(); 775 } 776 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 812 const AtomicString& DocumentLoader::mimeType() const 813 { 814 if (m_writer) 815 return m_writer->mimeType(); 816 return m_response.mimeType(); 817 } 818 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. 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