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 "DocumentLoader.h" 32 33#include "ApplicationCacheHost.h" 34#include "ArchiveResourceCollection.h" 35#include "CachedPage.h" 36#include "CachedRawResource.h" 37#include "CachedResourceLoader.h" 38#include "DOMWindow.h" 39#include "Document.h" 40#include "DocumentParser.h" 41#include "DocumentWriter.h" 42#include "Event.h" 43#include "FormState.h" 44#include "Frame.h" 45#include "FrameLoader.h" 46#include "FrameLoaderClient.h" 47#include "FrameTree.h" 48#include "HTMLFormElement.h" 49#include "HTMLFrameOwnerElement.h" 50#include "HistoryItem.h" 51#include "IconController.h" 52#include "InspectorInstrumentation.h" 53#include "Logging.h" 54#include "MemoryCache.h" 55#include "Page.h" 56#include "PolicyChecker.h" 57#include "ProgressTracker.h" 58#include "ResourceBuffer.h" 59#include "SchemeRegistry.h" 60#include "SecurityPolicy.h" 61#include "Settings.h" 62#include "SubresourceLoader.h" 63#include "TextResourceDecoder.h" 64#include <wtf/Assertions.h> 65#include <wtf/text/CString.h> 66#include <wtf/text/WTFString.h> 67#include <wtf/unicode/Unicode.h> 68 69#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 70#include "ArchiveFactory.h" 71#endif 72 73#if USE(CONTENT_FILTERING) 74#include "ContentFilter.h" 75#endif 76 77namespace WebCore { 78 79static void cancelAll(const ResourceLoaderSet& loaders) 80{ 81 Vector<RefPtr<ResourceLoader> > loadersCopy; 82 copyToVector(loaders, loadersCopy); 83 size_t size = loadersCopy.size(); 84 for (size_t i = 0; i < size; ++i) 85 loadersCopy[i]->cancel(); 86} 87 88static void setAllDefersLoading(const ResourceLoaderSet& loaders, bool defers) 89{ 90 Vector<RefPtr<ResourceLoader> > loadersCopy; 91 copyToVector(loaders, loadersCopy); 92 size_t size = loadersCopy.size(); 93 for (size_t i = 0; i < size; ++i) 94 loadersCopy[i]->setDefersLoading(defers); 95} 96 97DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData& substituteData) 98 : m_deferMainResourceDataLoad(true) 99 , m_frame(0) 100 , m_cachedResourceLoader(CachedResourceLoader::create(this)) 101 , m_writer(m_frame) 102 , m_originalRequest(req) 103 , m_substituteData(substituteData) 104 , m_originalRequestCopy(req) 105 , m_request(req) 106 , m_originalSubstituteDataWasValid(substituteData.isValid()) 107 , m_committed(false) 108 , m_isStopping(false) 109 , m_gotFirstByte(false) 110 , m_isClientRedirect(false) 111 , m_isLoadingMultipartContent(false) 112 , m_wasOnloadHandled(false) 113 , m_stopRecordingResponses(false) 114 , m_substituteResourceDeliveryTimer(this, &DocumentLoader::substituteResourceDeliveryTimerFired) 115 , m_didCreateGlobalHistoryEntry(false) 116 , m_loadingMainResource(false) 117 , m_timeOfLastDataReceived(0.0) 118 , m_identifierForLoadWithoutResourceLoader(0) 119 , m_dataLoadTimer(this, &DocumentLoader::handleSubstituteDataLoadNow) 120 , m_waitingForContentPolicy(false) 121 , m_applicationCacheHost(adoptPtr(new ApplicationCacheHost(this))) 122{ 123} 124 125FrameLoader* DocumentLoader::frameLoader() const 126{ 127 if (!m_frame) 128 return 0; 129 return m_frame->loader(); 130} 131 132ResourceLoader* DocumentLoader::mainResourceLoader() const 133{ 134 return m_mainResource ? m_mainResource->loader() : 0; 135} 136 137DocumentLoader::~DocumentLoader() 138{ 139 ASSERT(!m_frame || frameLoader()->activeDocumentLoader() != this || !isLoading()); 140 if (m_iconLoadDecisionCallback) 141 m_iconLoadDecisionCallback->invalidate(); 142 if (m_iconDataCallback) 143 m_iconDataCallback->invalidate(); 144 m_cachedResourceLoader->clearDocumentLoader(); 145 146 clearMainResource(); 147} 148 149PassRefPtr<ResourceBuffer> DocumentLoader::mainResourceData() const 150{ 151 if (m_substituteData.isValid()) 152 return ResourceBuffer::create(m_substituteData.content()->data(), m_substituteData.content()->size()); 153 if (m_mainResource) 154 return m_mainResource->resourceBuffer(); 155 return 0; 156} 157 158Document* DocumentLoader::document() const 159{ 160 if (m_frame && m_frame->loader()->documentLoader() == this) 161 return m_frame->document(); 162 return 0; 163} 164 165const ResourceRequest& DocumentLoader::originalRequest() const 166{ 167 return m_originalRequest; 168} 169 170const ResourceRequest& DocumentLoader::originalRequestCopy() const 171{ 172 return m_originalRequestCopy; 173} 174 175const ResourceRequest& DocumentLoader::request() const 176{ 177 return m_request; 178} 179 180ResourceRequest& DocumentLoader::request() 181{ 182 return m_request; 183} 184 185const KURL& DocumentLoader::url() const 186{ 187 return request().url(); 188} 189 190void DocumentLoader::replaceRequestURLForSameDocumentNavigation(const KURL& url) 191{ 192 m_originalRequestCopy.setURL(url); 193 m_request.setURL(url); 194} 195 196void DocumentLoader::setRequest(const ResourceRequest& req) 197{ 198 // Replacing an unreachable URL with alternate content looks like a server-side 199 // redirect at this point, but we can replace a committed dataSource. 200 bool handlingUnreachableURL = false; 201 202 handlingUnreachableURL = m_substituteData.isValid() && !m_substituteData.failingURL().isEmpty(); 203 204 if (handlingUnreachableURL) 205 m_committed = false; 206 207 // We should never be getting a redirect callback after the data 208 // source is committed, except in the unreachable URL case. It 209 // would be a WebFoundation bug if it sent a redirect callback after commit. 210 ASSERT(!m_committed); 211 212 m_request = req; 213} 214 215void DocumentLoader::setMainDocumentError(const ResourceError& error) 216{ 217 m_mainDocumentError = error; 218 frameLoader()->client()->setMainDocumentError(this, error); 219} 220 221void DocumentLoader::mainReceivedError(const ResourceError& error) 222{ 223 ASSERT(!error.isNull()); 224 225 if (m_identifierForLoadWithoutResourceLoader) { 226 ASSERT(!mainResourceLoader()); 227 frameLoader()->client()->dispatchDidFailLoading(this, m_identifierForLoadWithoutResourceLoader, error); 228 } 229 230 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. 231 // See <rdar://problem/6304600> for more details. 232#if !USE(CF) 233 ASSERT(!mainResourceLoader() || !mainResourceLoader()->defersLoading()); 234#endif 235 236 m_applicationCacheHost->failedLoadingMainResource(); 237 238 if (!frameLoader()) 239 return; 240 setMainDocumentError(error); 241 clearMainResourceLoader(); 242 frameLoader()->receivedMainResourceError(error); 243} 244 245// Cancels the data source's pending loads. Conceptually, a data source only loads 246// one document at a time, but one document may have many related resources. 247// stopLoading will stop all loads initiated by the data source, 248// but not loads initiated by child frames' data sources -- that's the WebFrame's job. 249void DocumentLoader::stopLoading() 250{ 251 RefPtr<Frame> protectFrame(m_frame); 252 RefPtr<DocumentLoader> protectLoader(this); 253 254 // In some rare cases, calling FrameLoader::stopLoading could cause isLoading() to return false. 255 // (This can happen when there's a single XMLHttpRequest currently loading and stopLoading causes it 256 // to stop loading. Because of this, we need to save it so we don't return early. 257 bool loading = isLoading(); 258 259 if (m_committed) { 260 // Attempt to stop the frame if the document loader is loading, or if it is done loading but 261 // still parsing. Failure to do so can cause a world leak. 262 Document* doc = m_frame->document(); 263 264 if (loading || doc->parsing()) 265 m_frame->loader()->stopLoading(UnloadEventPolicyNone); 266 } 267 268 // Always cancel multipart loaders 269 cancelAll(m_multipartSubresourceLoaders); 270 271 // Appcache uses ResourceHandle directly, DocumentLoader doesn't count these loads. 272 m_applicationCacheHost->stopLoadingInFrame(m_frame); 273 274#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 275 clearArchiveResources(); 276#endif 277 278 if (!loading) { 279 // If something above restarted loading we might run into mysterious crashes like 280 // https://bugs.webkit.org/show_bug.cgi?id=62764 and <rdar://problem/9328684> 281 ASSERT(!isLoading()); 282 return; 283 } 284 285 // We might run in to infinite recursion if we're stopping loading as the result of 286 // detaching from the frame, so break out of that recursion here. 287 // See <rdar://problem/9673866> for more details. 288 if (m_isStopping) 289 return; 290 291 m_isStopping = true; 292 293 FrameLoader* frameLoader = DocumentLoader::frameLoader(); 294 295 if (isLoadingMainResource()) { 296 // Stop the main resource loader and let it send the cancelled message. 297 cancelMainResourceLoad(frameLoader->cancelledError(m_request)); 298 } else if (!m_subresourceLoaders.isEmpty()) 299 // The main resource loader already finished loading. Set the cancelled error on the 300 // document and let the subresourceLoaders send individual cancelled messages below. 301 setMainDocumentError(frameLoader->cancelledError(m_request)); 302 else 303 // If there are no resource loaders, we need to manufacture a cancelled message. 304 // (A back/forward navigation has no resource loaders because its resources are cached.) 305 mainReceivedError(frameLoader->cancelledError(m_request)); 306 307 // We always need to explicitly cancel the Document's parser when stopping the load. 308 // Otherwise cancelling the parser while starting the next page load might result 309 // in unexpected side effects such as erroneous event dispatch. ( http://webkit.org/b/117112 ) 310 if (Document* document = this->document()) 311 document->cancelParsing(); 312 313 stopLoadingSubresources(); 314 stopLoadingPlugIns(); 315 316 m_isStopping = false; 317} 318 319void DocumentLoader::commitIfReady() 320{ 321 if (!m_committed) { 322 m_committed = true; 323 frameLoader()->commitProvisionalLoad(); 324 } 325} 326 327bool DocumentLoader::isLoading() const 328{ 329 // FIXME: This should always be enabled, but it seems to cause 330 // http/tests/security/feed-urls-from-remote.html to timeout on Mac WK1 331 // see http://webkit.org/b/110554 and http://webkit.org/b/110401 332#if ENABLE(THREADED_HTML_PARSER) 333 if (document() && document()->hasActiveParser()) 334 return true; 335#endif 336 return isLoadingMainResource() || !m_subresourceLoaders.isEmpty() || !m_plugInStreamLoaders.isEmpty(); 337} 338 339void DocumentLoader::notifyFinished(CachedResource* resource) 340{ 341 ASSERT_UNUSED(resource, m_mainResource == resource); 342 ASSERT(m_mainResource); 343 if (!m_mainResource->errorOccurred() && !m_mainResource->wasCanceled()) { 344 finishedLoading(m_mainResource->loadFinishTime()); 345 return; 346 } 347 348 if (m_request.cachePolicy() == ReturnCacheDataDontLoad && !m_mainResource->wasCanceled()) { 349 frameLoader()->retryAfterFailedCacheOnlyMainResourceLoad(); 350 return; 351 } 352 353 mainReceivedError(m_mainResource->resourceError()); 354} 355 356void DocumentLoader::finishedLoading(double finishTime) 357{ 358 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. 359 // See <rdar://problem/6304600> for more details. 360#if !USE(CF) 361 ASSERT(!m_frame->page()->defersLoading() || InspectorInstrumentation::isDebuggerPaused(m_frame)); 362#endif 363 364 RefPtr<DocumentLoader> protect(this); 365 366 if (m_identifierForLoadWithoutResourceLoader) { 367 // A didFinishLoading delegate might try to cancel the load (despite it 368 // being finished). Clear m_identifierForLoadWithoutResourceLoader 369 // before calling dispatchDidFinishLoading so that we don't later try to 370 // cancel the already-finished substitute load. 371 unsigned long identifier = m_identifierForLoadWithoutResourceLoader; 372 m_identifierForLoadWithoutResourceLoader = 0; 373 frameLoader()->notifier()->dispatchDidFinishLoading(this, identifier, finishTime); 374 } 375 376#if USE(CONTENT_FILTERING) 377 if (m_contentFilter && m_contentFilter->needsMoreData()) { 378 m_contentFilter->finishedAddingData(); 379 int length; 380 const char* data = m_contentFilter->getReplacementData(length); 381 if (data) 382 dataReceived(m_mainResource.get(), data, length); 383 } 384#endif 385 386 maybeFinishLoadingMultipartContent(); 387 388 double responseEndTime = finishTime; 389 if (!responseEndTime) 390 responseEndTime = m_timeOfLastDataReceived; 391 if (!responseEndTime) 392 responseEndTime = monotonicallyIncreasingTime(); 393 timing()->setResponseEnd(responseEndTime); 394 395 commitIfReady(); 396 if (!frameLoader()) 397 return; 398 399 if (!maybeCreateArchive()) { 400 // If this is an empty document, it will not have actually been created yet. Commit dummy data so that 401 // DocumentWriter::begin() gets called and creates the Document. 402 if (!m_gotFirstByte) 403 commitData(0, 0); 404 frameLoader()->client()->finishedLoading(this); 405 } 406 407 m_writer.end(); 408 if (!m_mainDocumentError.isNull()) 409 return; 410 clearMainResourceLoader(); 411 if (!frameLoader()->stateMachine()->creatingInitialEmptyDocument()) 412 frameLoader()->checkLoadComplete(); 413 414 // If the document specified an application cache manifest, it violates the author's intent if we store it in the memory cache 415 // and deny the appcache the chance to intercept it in the future, so remove from the memory cache. 416 if (m_frame) { 417 if (m_mainResource && m_frame->document()->hasManifest()) 418 memoryCache()->remove(m_mainResource.get()); 419 } 420 m_applicationCacheHost->finishedLoadingMainResource(); 421} 422 423bool DocumentLoader::isPostOrRedirectAfterPost(const ResourceRequest& newRequest, const ResourceResponse& redirectResponse) 424{ 425 if (newRequest.httpMethod() == "POST") 426 return true; 427 428 int status = redirectResponse.httpStatusCode(); 429 if (((status >= 301 && status <= 303) || status == 307) 430 && m_originalRequest.httpMethod() == "POST") 431 return true; 432 433 return false; 434} 435 436void DocumentLoader::handleSubstituteDataLoadNow(DocumentLoaderTimer*) 437{ 438 KURL url = m_substituteData.responseURL(); 439 if (url.isEmpty()) 440 url = m_request.url(); 441 ResourceResponse response(url, m_substituteData.mimeType(), m_substituteData.content()->size(), m_substituteData.textEncoding(), ""); 442 responseReceived(0, response); 443} 444 445void DocumentLoader::startDataLoadTimer() 446{ 447 m_dataLoadTimer.startOneShot(0); 448 449#if HAVE(RUNLOOP_TIMER) 450 if (SchedulePairHashSet* scheduledPairs = m_frame->page()->scheduledRunLoopPairs()) 451 m_dataLoadTimer.schedule(*scheduledPairs); 452#endif 453} 454 455void DocumentLoader::handleSubstituteDataLoadSoon() 456{ 457 if (m_deferMainResourceDataLoad) 458 startDataLoadTimer(); 459 else 460 handleSubstituteDataLoadNow(0); 461} 462 463void DocumentLoader::redirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse) 464{ 465 ASSERT_UNUSED(resource, resource == m_mainResource); 466 willSendRequest(request, redirectResponse); 467} 468 469void DocumentLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) 470{ 471 // Note that there are no asserts here as there are for the other callbacks. This is due to the 472 // fact that this "callback" is sent when starting every load, and the state of callback 473 // deferrals plays less of a part in this function in preventing the bad behavior deferring 474 // callbacks is meant to prevent. 475 ASSERT(!newRequest.isNull()); 476 477 if (!frameLoader()->checkIfFormActionAllowedByCSP(newRequest.url())) { 478 cancelMainResourceLoad(frameLoader()->cancelledError(newRequest)); 479 return; 480 } 481 482 ASSERT(timing()->fetchStart()); 483 if (!redirectResponse.isNull()) { 484 // If the redirecting url is not allowed to display content from the target origin, 485 // then block the redirect. 486 RefPtr<SecurityOrigin> redirectingOrigin = SecurityOrigin::create(redirectResponse.url()); 487 if (!redirectingOrigin->canDisplay(newRequest.url())) { 488 FrameLoader::reportLocalLoadFailed(m_frame, newRequest.url().string()); 489 cancelMainResourceLoad(frameLoader()->cancelledError(newRequest)); 490 return; 491 } 492 timing()->addRedirect(redirectResponse.url(), newRequest.url()); 493 } 494 495 // Update cookie policy base URL as URL changes, except for subframes, which use the 496 // URL of the main frame which doesn't change when we redirect. 497 if (frameLoader()->isLoadingMainFrame()) 498 newRequest.setFirstPartyForCookies(newRequest.url()); 499 500 // If we're fielding a redirect in response to a POST, force a load from origin, since 501 // this is a common site technique to return to a page viewing some data that the POST 502 // just modified. 503 // Also, POST requests always load from origin, but this does not affect subresources. 504 if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse)) 505 newRequest.setCachePolicy(ReloadIgnoringCacheData); 506 507 Frame* top = m_frame->tree()->top(); 508 if (top != m_frame) { 509 if (!frameLoader()->mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), newRequest.url())) { 510 cancelMainResourceLoad(frameLoader()->cancelledError(newRequest)); 511 return; 512 } 513 } 514 515 setRequest(newRequest); 516 517 if (!redirectResponse.isNull()) { 518 // We checked application cache for initial URL, now we need to check it for redirected one. 519 ASSERT(!m_substituteData.isValid()); 520 m_applicationCacheHost->maybeLoadMainResourceForRedirect(newRequest, m_substituteData); 521 if (m_substituteData.isValid()) 522 m_identifierForLoadWithoutResourceLoader = mainResourceLoader()->identifier(); 523 } 524 525 // FIXME: Ideally we'd stop the I/O until we hear back from the navigation policy delegate 526 // listener. But there's no way to do that in practice. So instead we cancel later if the 527 // listener tells us to. In practice that means the navigation policy needs to be decided 528 // synchronously for these redirect cases. 529 if (!redirectResponse.isNull()) 530 frameLoader()->policyChecker()->checkNavigationPolicy(newRequest, callContinueAfterNavigationPolicy, this); 531} 532 533void DocumentLoader::callContinueAfterNavigationPolicy(void* argument, const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) 534{ 535 static_cast<DocumentLoader*>(argument)->continueAfterNavigationPolicy(request, shouldContinue); 536} 537 538void DocumentLoader::continueAfterNavigationPolicy(const ResourceRequest&, bool shouldContinue) 539{ 540 if (!shouldContinue) 541 stopLoadingForPolicyChange(); 542 else if (m_substituteData.isValid()) { 543 // A redirect resulted in loading substitute data. 544 ASSERT(timing()->redirectCount()); 545 546 // We need to remove our reference to the CachedResource in favor of a SubstituteData load. 547 // This will probably trigger the cancellation of the CachedResource's underlying ResourceLoader, though there is a 548 // small chance that the resource is being loaded by a different Frame, preventing the ResourceLoader from being cancelled. 549 // If the ResourceLoader is indeed cancelled, it would normally send resource load callbacks. 550 // However, from an API perspective, this isn't a cancellation. Therefore, sever our relationship with the network load, 551 // but prevent the ResourceLoader from sending ResourceLoadNotifier callbacks. 552 RefPtr<ResourceLoader> resourceLoader = mainResourceLoader(); 553 ASSERT(resourceLoader->shouldSendResourceLoadCallbacks()); 554 resourceLoader->setSendCallbackPolicy(DoNotSendCallbacks); 555 clearMainResource(); 556 resourceLoader->setSendCallbackPolicy(SendCallbacks); 557 handleSubstituteDataLoadSoon(); 558 } 559} 560 561void DocumentLoader::responseReceived(CachedResource* resource, const ResourceResponse& response) 562{ 563 ASSERT_UNUSED(resource, m_mainResource == resource); 564 RefPtr<DocumentLoader> protect(this); 565 bool willLoadFallback = m_applicationCacheHost->maybeLoadFallbackForMainResponse(request(), response); 566 567 // The memory cache doesn't understand the application cache or its caching rules. So if a main resource is served 568 // from the application cache, ensure we don't save the result for future use. 569 if (willLoadFallback) 570 memoryCache()->remove(m_mainResource.get()); 571 572 if (willLoadFallback) 573 return; 574 575 DEFINE_STATIC_LOCAL(AtomicString, xFrameOptionHeader, ("x-frame-options", AtomicString::ConstructFromLiteral)); 576 HTTPHeaderMap::const_iterator it = response.httpHeaderFields().find(xFrameOptionHeader); 577 if (it != response.httpHeaderFields().end()) { 578 String content = it->value; 579 ASSERT(m_mainResource); 580 unsigned long identifier = m_identifierForLoadWithoutResourceLoader ? m_identifierForLoadWithoutResourceLoader : m_mainResource->identifier(); 581 ASSERT(identifier); 582 if (frameLoader()->shouldInterruptLoadForXFrameOptions(content, response.url(), identifier)) { 583 InspectorInstrumentation::continueAfterXFrameOptionsDenied(m_frame, this, identifier, response); 584 String message = "Refused to display '" + response.url().stringCenterEllipsizedToLength() + "' in a frame because it set 'X-Frame-Options' to '" + content + "'."; 585 frame()->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message, identifier); 586 frame()->document()->enforceSandboxFlags(SandboxOrigin); 587 if (HTMLFrameOwnerElement* ownerElement = frame()->ownerElement()) 588 ownerElement->dispatchEvent(Event::create(eventNames().loadEvent, false, false)); 589 590 // The load event might have detached this frame. In that case, the load will already have been cancelled during detach. 591 if (frameLoader()) 592 cancelMainResourceLoad(frameLoader()->cancelledError(m_request)); 593 return; 594 } 595 } 596 597 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. 598 // See <rdar://problem/6304600> for more details. 599#if !USE(CF) 600 ASSERT(!mainResourceLoader() || !mainResourceLoader()->defersLoading()); 601#endif 602 603 if (m_isLoadingMultipartContent) { 604 setupForReplace(); 605 m_mainResource->clear(); 606 } else if (response.isMultipart()) { 607 FeatureObserver::observe(m_frame->document(), FeatureObserver::MultipartMainResource); 608 m_isLoadingMultipartContent = true; 609 } 610 611 m_response = response; 612 613 if (m_identifierForLoadWithoutResourceLoader) { 614 addResponse(m_response); 615 frameLoader()->notifier()->dispatchDidReceiveResponse(this, m_identifierForLoadWithoutResourceLoader, m_response, 0); 616 } 617 618 ASSERT(!m_waitingForContentPolicy); 619 m_waitingForContentPolicy = true; 620 621 // Always show content with valid substitute data. 622 if (m_substituteData.isValid()) { 623 continueAfterContentPolicy(PolicyUse); 624 return; 625 } 626 627#if ENABLE(FTPDIR) 628 // Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it 629 Settings* settings = m_frame->settings(); 630 if (settings && settings->forceFTPDirectoryListings() && m_response.mimeType() == "application/x-ftp-directory") { 631 continueAfterContentPolicy(PolicyUse); 632 return; 633 } 634#endif 635 636#if USE(CONTENT_FILTERING) 637 if (response.url().protocolIs("https") && ContentFilter::isEnabled()) 638 m_contentFilter = ContentFilter::create(response); 639#endif 640 641 frameLoader()->policyChecker()->checkContentPolicy(m_response, callContinueAfterContentPolicy, this); 642} 643 644void DocumentLoader::callContinueAfterContentPolicy(void* argument, PolicyAction policy) 645{ 646 static_cast<DocumentLoader*>(argument)->continueAfterContentPolicy(policy); 647} 648 649void DocumentLoader::continueAfterContentPolicy(PolicyAction policy) 650{ 651 ASSERT(m_waitingForContentPolicy); 652 m_waitingForContentPolicy = false; 653 if (isStopping()) 654 return; 655 656 KURL url = m_request.url(); 657 const String& mimeType = m_response.mimeType(); 658 659 switch (policy) { 660 case PolicyUse: { 661 // Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255). 662 bool isRemoteWebArchive = (equalIgnoringCase("application/x-webarchive", mimeType) 663#if PLATFORM(GTK) 664 || equalIgnoringCase("message/rfc822", mimeType) 665#endif 666 || equalIgnoringCase("multipart/related", mimeType)) 667 && !m_substituteData.isValid() && !SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol()); 668 if (!frameLoader()->client()->canShowMIMEType(mimeType) || isRemoteWebArchive) { 669 frameLoader()->policyChecker()->cannotShowMIMEType(m_response); 670 // Check reachedTerminalState since the load may have already been canceled inside of _handleUnimplementablePolicyWithErrorCode::. 671 stopLoadingForPolicyChange(); 672 return; 673 } 674 break; 675 } 676 677 case PolicyDownload: { 678 // m_mainResource can be null, e.g. when loading a substitute resource from application cache. 679 if (!m_mainResource) { 680 mainReceivedError(frameLoader()->client()->cannotShowURLError(m_request)); 681 return; 682 } 683 684 if (ResourceLoader* mainResourceLoader = this->mainResourceLoader()) 685 InspectorInstrumentation::continueWithPolicyDownload(m_frame, this, mainResourceLoader->identifier(), m_response); 686 687 // When starting the request, we didn't know that it would result in download and not navigation. Now we know that main document URL didn't change. 688 // Download may use this knowledge for purposes unrelated to cookies, notably for setting file quarantine data. 689 frameLoader()->setOriginalURLForDownloadRequest(m_request); 690 frameLoader()->client()->convertMainResourceLoadToDownload(this, m_request, m_response); 691 692 // It might have gone missing 693 if (mainResourceLoader()) 694 mainResourceLoader()->didFail(interruptedForPolicyChangeError()); 695 return; 696 } 697 case PolicyIgnore: 698 if (ResourceLoader* mainResourceLoader = this->mainResourceLoader()) 699 InspectorInstrumentation::continueWithPolicyIgnore(m_frame, this, mainResourceLoader->identifier(), m_response); 700 stopLoadingForPolicyChange(); 701 return; 702 703 default: 704 ASSERT_NOT_REACHED(); 705 } 706 707 if (m_response.isHTTP()) { 708 int status = m_response.httpStatusCode(); 709 if (status < 200 || status >= 300) { 710 bool hostedByObject = frameLoader()->isHostedByObjectElement(); 711 712 frameLoader()->handleFallbackContent(); 713 // object elements are no longer rendered after we fallback, so don't 714 // keep trying to process data from their load 715 716 if (hostedByObject) 717 cancelMainResourceLoad(frameLoader()->cancelledError(m_request)); 718 } 719 } 720 721 if (!isStopping() && m_substituteData.isValid()) { 722 if (m_substituteData.content()->size()) 723 dataReceived(0, m_substituteData.content()->data(), m_substituteData.content()->size()); 724 if (isLoadingMainResource()) 725 finishedLoading(0); 726 } 727} 728 729void DocumentLoader::commitLoad(const char* data, int length) 730{ 731 // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource 732 // by starting a new load, so retain temporarily. 733 RefPtr<Frame> protectFrame(m_frame); 734 RefPtr<DocumentLoader> protectLoader(this); 735 736 commitIfReady(); 737 FrameLoader* frameLoader = DocumentLoader::frameLoader(); 738 if (!frameLoader) 739 return; 740#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 741 if (ArchiveFactory::isArchiveMimeType(response().mimeType())) 742 return; 743#endif 744 frameLoader->client()->committedLoad(this, data, length); 745} 746 747ResourceError DocumentLoader::interruptedForPolicyChangeError() const 748{ 749 return frameLoader()->client()->interruptedForPolicyChangeError(request()); 750} 751 752void DocumentLoader::stopLoadingForPolicyChange() 753{ 754 ResourceError error = interruptedForPolicyChangeError(); 755 error.setIsCancellation(true); 756 cancelMainResourceLoad(error); 757} 758 759void DocumentLoader::commitData(const char* bytes, size_t length) 760{ 761 if (!m_gotFirstByte) { 762 m_gotFirstByte = true; 763 m_writer.begin(documentURL(), false); 764 m_writer.setDocumentWasLoadedAsPartOfNavigation(); 765 766 if (SecurityPolicy::allowSubstituteDataAccessToLocal() && m_originalSubstituteDataWasValid) { 767 // If this document was loaded with substituteData, then the document can 768 // load local resources. See https://bugs.webkit.org/show_bug.cgi?id=16756 769 // and https://bugs.webkit.org/show_bug.cgi?id=19760 for further 770 // discussion. 771 m_frame->document()->securityOrigin()->grantLoadLocalResources(); 772 } 773 774 if (frameLoader()->stateMachine()->creatingInitialEmptyDocument()) 775 return; 776 777#if ENABLE(MHTML) 778 // The origin is the MHTML file, we need to set the base URL to the document encoded in the MHTML so 779 // relative URLs are resolved properly. 780 if (m_archive && m_archive->type() == Archive::MHTML) 781 m_frame->document()->setBaseURLOverride(m_archive->mainResource()->url()); 782#endif 783 784 // Call receivedFirstData() exactly once per load. We should only reach this point multiple times 785 // for multipart loads, and FrameLoader::isReplacing() will be true after the first time. 786 if (!isMultipartReplacingLoad()) 787 frameLoader()->receivedFirstData(); 788 789 bool userChosen = true; 790 String encoding = overrideEncoding(); 791 if (encoding.isNull()) { 792 userChosen = false; 793 encoding = response().textEncodingName(); 794#if ENABLE(WEB_ARCHIVE) 795 if (m_archive && m_archive->type() == Archive::WebArchive) 796 encoding = m_archive->mainResource()->textEncoding(); 797#endif 798 } 799 m_writer.setEncoding(encoding, userChosen); 800 } 801 ASSERT(m_frame->document()->parsing()); 802 m_writer.addData(bytes, length); 803} 804 805void DocumentLoader::dataReceived(CachedResource* resource, const char* data, int length) 806{ 807 ASSERT(data); 808 ASSERT(length); 809 ASSERT_UNUSED(resource, resource == m_mainResource); 810 ASSERT(!m_response.isNull()); 811 812#if USE(CFNETWORK) || PLATFORM(MAC) 813 // Workaround for <rdar://problem/6060782> 814 if (m_response.isNull()) 815 m_response = ResourceResponse(KURL(), "text/html", 0, String(), String()); 816#endif 817 818 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. 819 // See <rdar://problem/6304600> for more details. 820#if !USE(CF) 821 ASSERT(!mainResourceLoader() || !mainResourceLoader()->defersLoading()); 822#endif 823 824#if USE(CONTENT_FILTERING) 825 bool loadWasBlockedBeforeFinishing = false; 826 if (m_contentFilter && m_contentFilter->needsMoreData()) { 827 m_contentFilter->addData(data, length); 828 829 if (m_contentFilter->needsMoreData()) { 830 // Since the filter still needs more data to make a decision, 831 // transition back to the committed state so that we don't partially 832 // load content that might later be blocked. 833 commitLoad(0, 0); 834 return; 835 } 836 837 data = m_contentFilter->getReplacementData(length); 838 loadWasBlockedBeforeFinishing = m_contentFilter->didBlockData(); 839 } 840#endif 841 842 if (m_identifierForLoadWithoutResourceLoader) 843 frameLoader()->notifier()->dispatchDidReceiveData(this, m_identifierForLoadWithoutResourceLoader, data, length, -1); 844 845 m_applicationCacheHost->mainResourceDataReceived(data, length, -1, false); 846 m_timeOfLastDataReceived = monotonicallyIncreasingTime(); 847 848 if (!isMultipartReplacingLoad()) 849 commitLoad(data, length); 850 851#if USE(CONTENT_FILTERING) 852 if (loadWasBlockedBeforeFinishing) 853 cancelMainResourceLoad(frameLoader()->cancelledError(m_request)); 854#endif 855} 856 857void DocumentLoader::setupForReplace() 858{ 859 if (!mainResourceData()) 860 return; 861 862 maybeFinishLoadingMultipartContent(); 863 maybeCreateArchive(); 864 m_writer.end(); 865 frameLoader()->setReplacing(); 866 m_gotFirstByte = false; 867 868 stopLoadingSubresources(); 869 stopLoadingPlugIns(); 870#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 871 clearArchiveResources(); 872#endif 873} 874 875void DocumentLoader::checkLoadComplete() 876{ 877 if (!m_frame || isLoading()) 878 return; 879#if !ENABLE(THREADED_HTML_PARSER) 880 // This ASSERT triggers with the threaded HTML parser. 881 // See https://bugs.webkit.org/show_bug.cgi?id=110937 882 ASSERT(this == frameLoader()->activeDocumentLoader()); 883#endif 884 m_frame->document()->domWindow()->finishedLoading(); 885} 886 887void DocumentLoader::setFrame(Frame* frame) 888{ 889 if (m_frame == frame) 890 return; 891 ASSERT(frame && !m_frame); 892 m_frame = frame; 893 m_writer.setFrame(frame); 894 attachToFrame(); 895} 896 897void DocumentLoader::attachToFrame() 898{ 899 ASSERT(m_frame); 900} 901 902void DocumentLoader::detachFromFrame() 903{ 904 ASSERT(m_frame); 905 RefPtr<Frame> protectFrame(m_frame); 906 RefPtr<DocumentLoader> protectLoader(this); 907 908 // It never makes sense to have a document loader that is detached from its 909 // frame have any loads active, so go ahead and kill all the loads. 910 stopLoading(); 911 if (m_mainResource && m_mainResource->hasClient(this)) 912 m_mainResource->removeClient(this); 913 914 m_applicationCacheHost->setDOMApplicationCache(0); 915 InspectorInstrumentation::loaderDetachedFromFrame(m_frame, this); 916 m_frame = 0; 917} 918 919void DocumentLoader::clearMainResourceLoader() 920{ 921 m_loadingMainResource = false; 922 if (this == frameLoader()->activeDocumentLoader()) 923 checkLoadComplete(); 924} 925 926bool DocumentLoader::isLoadingInAPISense() const 927{ 928 // Once a frame has loaded, we no longer need to consider subresources, 929 // but we still need to consider subframes. 930 if (frameLoader()->state() != FrameStateComplete) { 931 if (m_frame->settings()->needsIsLoadingInAPISenseQuirk() && !m_subresourceLoaders.isEmpty()) 932 return true; 933 934 Document* doc = m_frame->document(); 935 if ((isLoadingMainResource() || !m_frame->document()->loadEventFinished()) && isLoading()) 936 return true; 937 if (m_cachedResourceLoader->requestCount()) 938 return true; 939 if (doc->processingLoadEvent()) 940 return true; 941 if (doc->hasActiveParser()) 942 return true; 943 } 944 return frameLoader()->subframeIsLoading(); 945} 946 947bool DocumentLoader::maybeCreateArchive() 948{ 949#if !ENABLE(WEB_ARCHIVE) && !ENABLE(MHTML) 950 return false; 951#else 952 953 // Give the archive machinery a crack at this document. If the MIME type is not an archive type, it will return 0. 954 RefPtr<ResourceBuffer> mainResourceBuffer = mainResourceData(); 955 m_archive = ArchiveFactory::create(m_response.url(), mainResourceBuffer ? mainResourceBuffer->sharedBuffer() : 0, m_response.mimeType()); 956 if (!m_archive) 957 return false; 958 959 addAllArchiveResources(m_archive.get()); 960 ArchiveResource* mainResource = m_archive->mainResource(); 961 m_parsedArchiveData = mainResource->data(); 962 m_writer.setMIMEType(mainResource->mimeType()); 963 964 ASSERT(m_frame->document()); 965 commitData(mainResource->data()->data(), mainResource->data()->size()); 966 return true; 967#endif // !ENABLE(WEB_ARCHIVE) && !ENABLE(MHTML) 968} 969 970#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 971void DocumentLoader::setArchive(PassRefPtr<Archive> archive) 972{ 973 m_archive = archive; 974 addAllArchiveResources(m_archive.get()); 975} 976 977void DocumentLoader::addAllArchiveResources(Archive* archive) 978{ 979 if (!m_archiveResourceCollection) 980 m_archiveResourceCollection = adoptPtr(new ArchiveResourceCollection); 981 982 ASSERT(archive); 983 if (!archive) 984 return; 985 986 m_archiveResourceCollection->addAllResources(archive); 987} 988 989// FIXME: Adding a resource directly to a DocumentLoader/ArchiveResourceCollection seems like bad design, but is API some apps rely on. 990// Can we change the design in a manner that will let us deprecate that API without reducing functionality of those apps? 991void DocumentLoader::addArchiveResource(PassRefPtr<ArchiveResource> resource) 992{ 993 if (!m_archiveResourceCollection) 994 m_archiveResourceCollection = adoptPtr(new ArchiveResourceCollection); 995 996 ASSERT(resource); 997 if (!resource) 998 return; 999 1000 m_archiveResourceCollection->addResource(resource); 1001} 1002 1003PassRefPtr<Archive> DocumentLoader::popArchiveForSubframe(const String& frameName, const KURL& url) 1004{ 1005 return m_archiveResourceCollection ? m_archiveResourceCollection->popSubframeArchive(frameName, url) : PassRefPtr<Archive>(0); 1006} 1007 1008void DocumentLoader::clearArchiveResources() 1009{ 1010 m_archiveResourceCollection.clear(); 1011 m_substituteResourceDeliveryTimer.stop(); 1012} 1013 1014SharedBuffer* DocumentLoader::parsedArchiveData() const 1015{ 1016 return m_parsedArchiveData.get(); 1017} 1018#endif // ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 1019 1020ArchiveResource* DocumentLoader::archiveResourceForURL(const KURL& url) const 1021{ 1022 if (!m_archiveResourceCollection) 1023 return 0; 1024 1025 ArchiveResource* resource = m_archiveResourceCollection->archiveResourceForURL(url); 1026 1027 return resource && !resource->shouldIgnoreWhenUnarchiving() ? resource : 0; 1028} 1029 1030PassRefPtr<ArchiveResource> DocumentLoader::mainResource() const 1031{ 1032 const ResourceResponse& r = response(); 1033 1034 RefPtr<ResourceBuffer> mainResourceBuffer = mainResourceData(); 1035 RefPtr<SharedBuffer> data = mainResourceBuffer ? mainResourceBuffer->sharedBuffer() : 0; 1036 if (!data) 1037 data = SharedBuffer::create(); 1038 1039 return ArchiveResource::create(data, r.url(), r.mimeType(), r.textEncodingName(), frame()->tree()->uniqueName()); 1040} 1041 1042PassRefPtr<ArchiveResource> DocumentLoader::subresource(const KURL& url) const 1043{ 1044 if (!isCommitted()) 1045 return 0; 1046 1047 CachedResource* resource = m_cachedResourceLoader->cachedResource(url); 1048 if (!resource || !resource->isLoaded()) 1049 return archiveResourceForURL(url); 1050 1051 if (resource->type() == CachedResource::MainResource) 1052 return 0; 1053 1054 // FIXME: This has the side effect of making the resource non-purgeable. 1055 // It would be better if it didn't have this permanent effect. 1056 if (!resource->makePurgeable(false)) 1057 return 0; 1058 1059 ResourceBuffer* data = resource->resourceBuffer(); 1060 if (!data) 1061 return 0; 1062 1063 return ArchiveResource::create(data->sharedBuffer(), url, resource->response()); 1064} 1065 1066void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subresources) const 1067{ 1068 if (!isCommitted()) 1069 return; 1070 1071 const CachedResourceLoader::DocumentResourceMap& allResources = m_cachedResourceLoader->allCachedResources(); 1072 CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end(); 1073 for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) { 1074 RefPtr<ArchiveResource> subresource = this->subresource(KURL(ParsedURLString, it->value->url())); 1075 if (subresource) 1076 subresources.append(subresource.release()); 1077 } 1078 1079 return; 1080} 1081 1082void DocumentLoader::deliverSubstituteResourcesAfterDelay() 1083{ 1084 if (m_pendingSubstituteResources.isEmpty()) 1085 return; 1086 ASSERT(m_frame && m_frame->page()); 1087 if (m_frame->page()->defersLoading()) 1088 return; 1089 if (!m_substituteResourceDeliveryTimer.isActive()) 1090 m_substituteResourceDeliveryTimer.startOneShot(0); 1091} 1092 1093void DocumentLoader::substituteResourceDeliveryTimerFired(Timer<DocumentLoader>*) 1094{ 1095 if (m_pendingSubstituteResources.isEmpty()) 1096 return; 1097 ASSERT(m_frame && m_frame->page()); 1098 if (m_frame->page()->defersLoading()) 1099 return; 1100 1101 SubstituteResourceMap copy; 1102 copy.swap(m_pendingSubstituteResources); 1103 1104 SubstituteResourceMap::const_iterator end = copy.end(); 1105 for (SubstituteResourceMap::const_iterator it = copy.begin(); it != end; ++it) { 1106 RefPtr<ResourceLoader> loader = it->key; 1107 SubstituteResource* resource = it->value.get(); 1108 1109 if (resource) { 1110 SharedBuffer* data = resource->data(); 1111 1112 loader->didReceiveResponse(resource->response()); 1113 1114 // Calling ResourceLoader::didReceiveResponse can end up cancelling the load, 1115 // so we need to check if the loader has reached its terminal state. 1116 if (loader->reachedTerminalState()) 1117 return; 1118 1119 loader->didReceiveData(data->data(), data->size(), data->size(), DataPayloadWholeResource); 1120 1121 // Calling ResourceLoader::didReceiveData can end up cancelling the load, 1122 // so we need to check if the loader has reached its terminal state. 1123 if (loader->reachedTerminalState()) 1124 return; 1125 1126 loader->didFinishLoading(0); 1127 } else { 1128 // A null resource means that we should fail the load. 1129 // FIXME: Maybe we should use another error here - something like "not in cache". 1130 loader->didFail(loader->cannotShowURLError()); 1131 } 1132 } 1133} 1134 1135#ifndef NDEBUG 1136bool DocumentLoader::isSubstituteLoadPending(ResourceLoader* loader) const 1137{ 1138 return m_pendingSubstituteResources.contains(loader); 1139} 1140#endif 1141 1142void DocumentLoader::cancelPendingSubstituteLoad(ResourceLoader* loader) 1143{ 1144 if (m_pendingSubstituteResources.isEmpty()) 1145 return; 1146 m_pendingSubstituteResources.remove(loader); 1147 if (m_pendingSubstituteResources.isEmpty()) 1148 m_substituteResourceDeliveryTimer.stop(); 1149} 1150 1151#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML) 1152bool DocumentLoader::scheduleArchiveLoad(ResourceLoader* loader, const ResourceRequest& request) 1153{ 1154 if (ArchiveResource* resource = archiveResourceForURL(request.url())) { 1155 m_pendingSubstituteResources.set(loader, resource); 1156 deliverSubstituteResourcesAfterDelay(); 1157 return true; 1158 } 1159 1160 if (!m_archive) 1161 return false; 1162 1163 switch (m_archive->type()) { 1164#if ENABLE(WEB_ARCHIVE) 1165 case Archive::WebArchive: 1166 // WebArchiveDebugMode means we fail loads instead of trying to fetch them from the network if they're not in the archive. 1167 return m_frame->settings() && m_frame->settings()->webArchiveDebugModeEnabled() && ArchiveFactory::isArchiveMimeType(responseMIMEType()); 1168#endif 1169#if ENABLE(MHTML) 1170 case Archive::MHTML: 1171 return true; // Always fail the load for resources not included in the MHTML. 1172#endif 1173 default: 1174 return false; 1175 } 1176} 1177#endif // ENABLE(WEB_ARCHIVE) 1178 1179void DocumentLoader::addResponse(const ResourceResponse& r) 1180{ 1181 if (!m_stopRecordingResponses) 1182 m_responses.append(r); 1183} 1184 1185void DocumentLoader::stopRecordingResponses() 1186{ 1187 m_stopRecordingResponses = true; 1188 m_responses.shrinkToFit(); 1189} 1190 1191void DocumentLoader::setTitle(const StringWithDirection& title) 1192{ 1193 if (m_pageTitle == title) 1194 return; 1195 1196 frameLoader()->willChangeTitle(this); 1197 m_pageTitle = title; 1198 frameLoader()->didChangeTitle(this); 1199} 1200 1201KURL DocumentLoader::urlForHistory() const 1202{ 1203 // Return the URL to be used for history and B/F list. 1204 // Returns nil for WebDataProtocol URLs that aren't alternates 1205 // for unreachable URLs, because these can't be stored in history. 1206 if (m_substituteData.isValid()) 1207 return unreachableURL(); 1208 1209 return m_originalRequestCopy.url(); 1210} 1211 1212bool DocumentLoader::urlForHistoryReflectsFailure() const 1213{ 1214 return m_substituteData.isValid() || m_response.httpStatusCode() >= 400; 1215} 1216 1217const KURL& DocumentLoader::originalURL() const 1218{ 1219 return m_originalRequestCopy.url(); 1220} 1221 1222const KURL& DocumentLoader::requestURL() const 1223{ 1224 return request().url(); 1225} 1226 1227const KURL& DocumentLoader::responseURL() const 1228{ 1229 return m_response.url(); 1230} 1231 1232KURL DocumentLoader::documentURL() const 1233{ 1234 KURL url = substituteData().responseURL(); 1235#if ENABLE(WEB_ARCHIVE) 1236 if (url.isEmpty() && m_archive && m_archive->type() == Archive::WebArchive) 1237 url = m_archive->mainResource()->url(); 1238#endif 1239 if (url.isEmpty()) 1240 url = requestURL(); 1241 if (url.isEmpty()) 1242 url = m_response.url(); 1243 return url; 1244} 1245 1246const String& DocumentLoader::responseMIMEType() const 1247{ 1248 return m_response.mimeType(); 1249} 1250 1251const KURL& DocumentLoader::unreachableURL() const 1252{ 1253 return m_substituteData.failingURL(); 1254} 1255 1256void DocumentLoader::setDefersLoading(bool defers) 1257{ 1258 // Multiple frames may be loading the same main resource simultaneously. If deferral state changes, 1259 // each frame's DocumentLoader will try to send a setDefersLoading() to the same underlying ResourceLoader. Ensure only 1260 // the "owning" DocumentLoader does so, as setDefersLoading() is not resilient to setting the same value repeatedly. 1261 if (mainResourceLoader() && mainResourceLoader()->documentLoader() == this) 1262 mainResourceLoader()->setDefersLoading(defers); 1263 1264 setAllDefersLoading(m_subresourceLoaders, defers); 1265 setAllDefersLoading(m_plugInStreamLoaders, defers); 1266 if (!defers) 1267 deliverSubstituteResourcesAfterDelay(); 1268} 1269 1270void DocumentLoader::setMainResourceDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy) 1271{ 1272 if (m_mainResource) 1273 m_mainResource->setDataBufferingPolicy(dataBufferingPolicy); 1274} 1275 1276void DocumentLoader::stopLoadingPlugIns() 1277{ 1278 cancelAll(m_plugInStreamLoaders); 1279} 1280 1281void DocumentLoader::stopLoadingSubresources() 1282{ 1283 cancelAll(m_subresourceLoaders); 1284} 1285 1286void DocumentLoader::addSubresourceLoader(ResourceLoader* loader) 1287{ 1288 // The main resource's underlying ResourceLoader will ask to be added here. 1289 // It is much simpler to handle special casing of main resource loads if we don't 1290 // let it be added. In the main resource load case, mainResourceLoader() 1291 // will still be null at this point, but m_gotFirstByte should be false here if and only 1292 // if we are just starting the main resource load. 1293 if (!m_gotFirstByte) 1294 return; 1295 ASSERT(!m_subresourceLoaders.contains(loader)); 1296 ASSERT(!mainResourceLoader() || mainResourceLoader() != loader); 1297 m_subresourceLoaders.add(loader); 1298} 1299 1300void DocumentLoader::removeSubresourceLoader(ResourceLoader* loader) 1301{ 1302 ResourceLoaderSet::iterator it = m_subresourceLoaders.find(loader); 1303 if (it == m_subresourceLoaders.end()) 1304 return; 1305 m_subresourceLoaders.remove(it); 1306 checkLoadComplete(); 1307 if (Frame* frame = m_frame) 1308 frame->loader()->checkLoadComplete(); 1309} 1310 1311void DocumentLoader::addPlugInStreamLoader(ResourceLoader* loader) 1312{ 1313 m_plugInStreamLoaders.add(loader); 1314} 1315 1316void DocumentLoader::removePlugInStreamLoader(ResourceLoader* loader) 1317{ 1318 m_plugInStreamLoaders.remove(loader); 1319 checkLoadComplete(); 1320} 1321 1322bool DocumentLoader::isMultipartReplacingLoad() const 1323{ 1324 return isLoadingMultipartContent() && frameLoader()->isReplacing(); 1325} 1326 1327bool DocumentLoader::maybeLoadEmpty() 1328{ 1329 bool shouldLoadEmpty = !m_substituteData.isValid() && (m_request.url().isEmpty() || SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(m_request.url().protocol())); 1330 if (!shouldLoadEmpty && !frameLoader()->client()->representationExistsForURLScheme(m_request.url().protocol())) 1331 return false; 1332 1333 if (m_request.url().isEmpty() && !frameLoader()->stateMachine()->creatingInitialEmptyDocument()) 1334 m_request.setURL(blankURL()); 1335 String mimeType = shouldLoadEmpty ? "text/html" : frameLoader()->client()->generatedMIMETypeForURLScheme(m_request.url().protocol()); 1336 m_response = ResourceResponse(m_request.url(), mimeType, 0, String(), String()); 1337 finishedLoading(monotonicallyIncreasingTime()); 1338 return true; 1339} 1340 1341void DocumentLoader::startLoadingMainResource() 1342{ 1343 m_mainDocumentError = ResourceError(); 1344 timing()->markNavigationStart(); 1345 ASSERT(!m_mainResource); 1346 ASSERT(!m_loadingMainResource); 1347 m_loadingMainResource = true; 1348 1349 if (maybeLoadEmpty()) 1350 return; 1351 1352 // FIXME: Is there any way the extra fields could have not been added by now? 1353 // If not, it would be great to remove this line of code. 1354 // Note that currently, some requests may have incorrect extra fields even if this function has been called, 1355 // because we pass a wrong loadType (see FIXME in addExtraFieldsToMainResourceRequest()). 1356 frameLoader()->addExtraFieldsToMainResourceRequest(m_request); 1357 1358 ASSERT(timing()->navigationStart()); 1359 ASSERT(!timing()->fetchStart()); 1360 timing()->markFetchStart(); 1361 willSendRequest(m_request, ResourceResponse()); 1362 1363 // willSendRequest() may lead to our Frame being detached or cancelling the load via nulling the ResourceRequest. 1364 if (!m_frame || m_request.isNull()) 1365 return; 1366 1367 m_applicationCacheHost->maybeLoadMainResource(m_request, m_substituteData); 1368 1369 if (m_substituteData.isValid()) { 1370 m_identifierForLoadWithoutResourceLoader = m_frame->page()->progress()->createUniqueIdentifier(); 1371 frameLoader()->notifier()->assignIdentifierToInitialRequest(m_identifierForLoadWithoutResourceLoader, this, m_request); 1372 frameLoader()->notifier()->dispatchWillSendRequest(this, m_identifierForLoadWithoutResourceLoader, m_request, ResourceResponse()); 1373 handleSubstituteDataLoadSoon(); 1374 return; 1375 } 1376 1377 ResourceRequest request(m_request); 1378 DEFINE_STATIC_LOCAL(ResourceLoaderOptions, mainResourceLoadOptions, 1379 (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForAllCredentials, SkipSecurityCheck)); 1380 CachedResourceRequest cachedResourceRequest(request, mainResourceLoadOptions); 1381 m_mainResource = m_cachedResourceLoader->requestMainResource(cachedResourceRequest); 1382 if (!m_mainResource) { 1383 setRequest(ResourceRequest()); 1384 // If the load was aborted by clearing m_request, it's possible the ApplicationCacheHost 1385 // is now in a state where starting an empty load will be inconsistent. Replace it with 1386 // a new ApplicationCacheHost. 1387 m_applicationCacheHost = adoptPtr(new ApplicationCacheHost(this)); 1388 maybeLoadEmpty(); 1389 return; 1390 } 1391 1392 if (!mainResourceLoader()) { 1393 m_identifierForLoadWithoutResourceLoader = m_frame->page()->progress()->createUniqueIdentifier(); 1394 frameLoader()->notifier()->assignIdentifierToInitialRequest(m_identifierForLoadWithoutResourceLoader, this, request); 1395 frameLoader()->notifier()->dispatchWillSendRequest(this, m_identifierForLoadWithoutResourceLoader, request, ResourceResponse()); 1396 } 1397 m_mainResource->addClient(this); 1398 1399 // A bunch of headers are set when the underlying ResourceLoader is created, and m_request needs to include those. 1400 if (mainResourceLoader()) 1401 request = mainResourceLoader()->originalRequest(); 1402 // If there was a fragment identifier on m_request, the cache will have stripped it. m_request should include 1403 // the fragment identifier, so add that back in. 1404 if (equalIgnoringFragmentIdentifier(m_request.url(), request.url())) 1405 request.setURL(m_request.url()); 1406 setRequest(request); 1407} 1408 1409void DocumentLoader::cancelMainResourceLoad(const ResourceError& resourceError) 1410{ 1411 RefPtr<DocumentLoader> protect(this); 1412 ResourceError error = resourceError.isNull() ? frameLoader()->cancelledError(m_request) : resourceError; 1413 1414 m_dataLoadTimer.stop(); 1415 if (m_waitingForContentPolicy) { 1416 frameLoader()->policyChecker()->cancelCheck(); 1417 ASSERT(m_waitingForContentPolicy); 1418 m_waitingForContentPolicy = false; 1419 } 1420 1421 if (mainResourceLoader()) 1422 mainResourceLoader()->cancel(error); 1423 1424 clearMainResource(); 1425 1426 mainReceivedError(error); 1427} 1428 1429void DocumentLoader::clearMainResource() 1430{ 1431 if (m_mainResource && m_mainResource->hasClient(this)) 1432 m_mainResource->removeClient(this); 1433 1434 m_mainResource = 0; 1435} 1436 1437void DocumentLoader::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader) 1438{ 1439 m_multipartSubresourceLoaders.add(loader); 1440 m_subresourceLoaders.remove(loader); 1441 checkLoadComplete(); 1442 if (Frame* frame = m_frame) 1443 frame->loader()->checkLoadComplete(); 1444} 1445 1446void DocumentLoader::maybeFinishLoadingMultipartContent() 1447{ 1448 if (!isMultipartReplacingLoad()) 1449 return; 1450 1451 frameLoader()->setupForReplace(); 1452 m_committed = false; 1453 RefPtr<ResourceBuffer> resourceData = mainResourceData(); 1454 commitLoad(resourceData->data(), resourceData->size()); 1455} 1456 1457void DocumentLoader::iconLoadDecisionAvailable() 1458{ 1459 if (m_frame) 1460 m_frame->loader()->icon()->loadDecisionReceived(iconDatabase().synchronousLoadDecisionForIconURL(frameLoader()->icon()->url(), this)); 1461} 1462 1463static void iconLoadDecisionCallback(IconLoadDecision decision, void* context) 1464{ 1465 static_cast<DocumentLoader*>(context)->continueIconLoadWithDecision(decision); 1466} 1467 1468void DocumentLoader::getIconLoadDecisionForIconURL(const String& urlString) 1469{ 1470 if (m_iconLoadDecisionCallback) 1471 m_iconLoadDecisionCallback->invalidate(); 1472 m_iconLoadDecisionCallback = IconLoadDecisionCallback::create(this, iconLoadDecisionCallback); 1473 iconDatabase().loadDecisionForIconURL(urlString, m_iconLoadDecisionCallback); 1474} 1475 1476void DocumentLoader::continueIconLoadWithDecision(IconLoadDecision decision) 1477{ 1478 ASSERT(m_iconLoadDecisionCallback); 1479 m_iconLoadDecisionCallback = 0; 1480 if (m_frame) 1481 m_frame->loader()->icon()->continueLoadWithDecision(decision); 1482} 1483 1484static void iconDataCallback(SharedBuffer*, void*) 1485{ 1486 // FIXME: Implement this once we know what parts of WebCore actually need the icon data returned. 1487} 1488 1489void DocumentLoader::getIconDataForIconURL(const String& urlString) 1490{ 1491 if (m_iconDataCallback) 1492 m_iconDataCallback->invalidate(); 1493 m_iconDataCallback = IconDataCallback::create(this, iconDataCallback); 1494 iconDatabase().iconDataForIconURL(urlString, m_iconDataCallback); 1495} 1496 1497void DocumentLoader::handledOnloadEvents() 1498{ 1499 m_wasOnloadHandled = true; 1500 applicationCacheHost()->stopDeferringEvents(); 1501} 1502 1503} // namespace WebCore 1504