1 /* 2 * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 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 "MainResourceLoader.h" 32 33 #include "ApplicationCacheHost.h" 34 #include "DOMWindow.h" 35 #include "Document.h" 36 #include "DocumentLoadTiming.h" 37 #include "DocumentLoader.h" 38 #include "FormState.h" 39 #include "Frame.h" 40 #include "FrameLoader.h" 41 #include "FrameLoaderClient.h" 42 #include "HTMLFormElement.h" 43 #include "InspectorInstrumentation.h" 44 #include "Page.h" 45 #if PLATFORM(QT) 46 #include "PluginDatabase.h" 47 #endif 48 #include "ResourceError.h" 49 #include "ResourceHandle.h" 50 #include "ResourceLoadScheduler.h" 51 #include "SchemeRegistry.h" 52 #include "Settings.h" 53 #include <wtf/CurrentTime.h> 54 55 // FIXME: More that is in common with SubresourceLoader should move up into ResourceLoader. 56 57 namespace WebCore { 58 59 MainResourceLoader::MainResourceLoader(Frame* frame) 60 : ResourceLoader(frame, true, true) 61 , m_dataLoadTimer(this, &MainResourceLoader::handleDataLoadNow) 62 , m_loadingMultipartContent(false) 63 , m_waitingForContentPolicy(false) 64 , m_timeOfLastDataReceived(0.0) 65 { 66 } 67 68 MainResourceLoader::~MainResourceLoader() 69 { 70 } 71 72 PassRefPtr<MainResourceLoader> MainResourceLoader::create(Frame* frame) 73 { 74 return adoptRef(new MainResourceLoader(frame)); 75 } 76 77 void MainResourceLoader::receivedError(const ResourceError& error) 78 { 79 // Calling receivedMainResourceError will likely result in the last reference to this object to go away. 80 RefPtr<MainResourceLoader> protect(this); 81 RefPtr<Frame> protectFrame(m_frame); 82 83 // It is important that we call FrameLoader::receivedMainResourceError before calling 84 // FrameLoader::didFailToLoad because receivedMainResourceError clears out the relevant 85 // document loaders. Also, receivedMainResourceError ends up calling a FrameLoadDelegate method 86 // and didFailToLoad calls a ResourceLoadDelegate method and they need to be in the correct order. 87 frameLoader()->receivedMainResourceError(error, true); 88 89 if (!cancelled()) { 90 ASSERT(!reachedTerminalState()); 91 frameLoader()->notifier()->didFailToLoad(this, error); 92 93 releaseResources(); 94 } 95 96 ASSERT(reachedTerminalState()); 97 } 98 99 void MainResourceLoader::didCancel(const ResourceError& error) 100 { 101 m_dataLoadTimer.stop(); 102 103 // Calling receivedMainResourceError will likely result in the last reference to this object to go away. 104 RefPtr<MainResourceLoader> protect(this); 105 106 if (m_waitingForContentPolicy) { 107 frameLoader()->policyChecker()->cancelCheck(); 108 ASSERT(m_waitingForContentPolicy); 109 m_waitingForContentPolicy = false; 110 deref(); // balances ref in didReceiveResponse 111 } 112 frameLoader()->receivedMainResourceError(error, true); 113 ResourceLoader::didCancel(error); 114 } 115 116 ResourceError MainResourceLoader::interruptionForPolicyChangeError() const 117 { 118 return frameLoader()->interruptionForPolicyChangeError(request()); 119 } 120 121 void MainResourceLoader::stopLoadingForPolicyChange() 122 { 123 ResourceError error = interruptionForPolicyChangeError(); 124 error.setIsCancellation(true); 125 cancel(error); 126 } 127 128 void MainResourceLoader::callContinueAfterNavigationPolicy(void* argument, const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) 129 { 130 static_cast<MainResourceLoader*>(argument)->continueAfterNavigationPolicy(request, shouldContinue); 131 } 132 133 void MainResourceLoader::continueAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue) 134 { 135 if (!shouldContinue) 136 stopLoadingForPolicyChange(); 137 else if (m_substituteData.isValid()) { 138 // A redirect resulted in loading substitute data. 139 ASSERT(documentLoader()->timing()->redirectCount); 140 handle()->cancel(); 141 handleDataLoadSoon(request); 142 } 143 144 deref(); // balances ref in willSendRequest 145 } 146 147 bool MainResourceLoader::isPostOrRedirectAfterPost(const ResourceRequest& newRequest, const ResourceResponse& redirectResponse) 148 { 149 if (newRequest.httpMethod() == "POST") 150 return true; 151 152 int status = redirectResponse.httpStatusCode(); 153 if (((status >= 301 && status <= 303) || status == 307) 154 && frameLoader()->initialRequest().httpMethod() == "POST") 155 return true; 156 157 return false; 158 } 159 160 void MainResourceLoader::addData(const char* data, int length, bool allAtOnce) 161 { 162 ResourceLoader::addData(data, length, allAtOnce); 163 documentLoader()->receivedData(data, length); 164 } 165 166 void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) 167 { 168 // Note that there are no asserts here as there are for the other callbacks. This is due to the 169 // fact that this "callback" is sent when starting every load, and the state of callback 170 // deferrals plays less of a part in this function in preventing the bad behavior deferring 171 // callbacks is meant to prevent. 172 ASSERT(!newRequest.isNull()); 173 174 // The additional processing can do anything including possibly removing the last 175 // reference to this object; one example of this is 3266216. 176 RefPtr<MainResourceLoader> protect(this); 177 178 ASSERT(documentLoader()->timing()->fetchStart); 179 if (!redirectResponse.isNull()) { 180 DocumentLoadTiming* documentLoadTiming = documentLoader()->timing(); 181 182 // Check if the redirected url is allowed to access the redirecting url's timing information. 183 RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(newRequest.url()); 184 if (!securityOrigin->canRequest(redirectResponse.url())) 185 documentLoadTiming->hasCrossOriginRedirect = true; 186 187 documentLoadTiming->redirectCount++; 188 if (!documentLoadTiming->redirectStart) 189 documentLoadTiming->redirectStart = documentLoadTiming->fetchStart; 190 documentLoadTiming->redirectEnd = currentTime(); 191 documentLoadTiming->fetchStart = documentLoadTiming->redirectEnd; 192 } 193 194 // Update cookie policy base URL as URL changes, except for subframes, which use the 195 // URL of the main frame which doesn't change when we redirect. 196 if (frameLoader()->isLoadingMainFrame()) 197 newRequest.setFirstPartyForCookies(newRequest.url()); 198 199 // If we're fielding a redirect in response to a POST, force a load from origin, since 200 // this is a common site technique to return to a page viewing some data that the POST 201 // just modified. 202 // Also, POST requests always load from origin, but this does not affect subresources. 203 if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse)) 204 newRequest.setCachePolicy(ReloadIgnoringCacheData); 205 206 ResourceLoader::willSendRequest(newRequest, redirectResponse); 207 208 // Don't set this on the first request. It is set when the main load was started. 209 m_documentLoader->setRequest(newRequest); 210 211 Frame* top = m_frame->tree()->top(); 212 if (top != m_frame) 213 frameLoader()->checkIfDisplayInsecureContent(top->document()->securityOrigin(), newRequest.url()); 214 215 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 216 if (!redirectResponse.isNull()) { 217 // We checked application cache for initial URL, now we need to check it for redirected one. 218 ASSERT(!m_substituteData.isValid()); 219 documentLoader()->applicationCacheHost()->maybeLoadMainResourceForRedirect(newRequest, m_substituteData); 220 } 221 #endif 222 223 // FIXME: Ideally we'd stop the I/O until we hear back from the navigation policy delegate 224 // listener. But there's no way to do that in practice. So instead we cancel later if the 225 // listener tells us to. In practice that means the navigation policy needs to be decided 226 // synchronously for these redirect cases. 227 if (!redirectResponse.isNull()) { 228 ref(); // balanced by deref in continueAfterNavigationPolicy 229 frameLoader()->policyChecker()->checkNavigationPolicy(newRequest, callContinueAfterNavigationPolicy, this); 230 } 231 } 232 233 static bool shouldLoadAsEmptyDocument(const KURL& url) 234 { 235 #if PLATFORM(TORCHMOBILE) 236 return url.isEmpty() || (url.protocolIs("about") && equalIgnoringRef(url, blankURL())); 237 #else 238 return url.isEmpty() || SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(url.protocol()); 239 #endif 240 } 241 242 void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, const ResourceResponse& r) 243 { 244 KURL url = request().url(); 245 const String& mimeType = r.mimeType(); 246 247 switch (contentPolicy) { 248 case PolicyUse: { 249 // Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255). 250 bool isRemoteWebArchive = equalIgnoringCase("application/x-webarchive", mimeType) && !m_substituteData.isValid() && !url.isLocalFile(); 251 if (!frameLoader()->canShowMIMEType(mimeType) || isRemoteWebArchive) { 252 frameLoader()->policyChecker()->cannotShowMIMEType(r); 253 // Check reachedTerminalState since the load may have already been cancelled inside of _handleUnimplementablePolicyWithErrorCode::. 254 if (!reachedTerminalState()) 255 stopLoadingForPolicyChange(); 256 return; 257 } 258 break; 259 } 260 261 case PolicyDownload: 262 // m_handle can be null, e.g. when loading a substitute resource from application cache. 263 if (!m_handle) { 264 receivedError(cannotShowURLError()); 265 return; 266 } 267 InspectorInstrumentation::continueWithPolicyDownload(m_frame.get(), documentLoader(), identifier(), r); 268 frameLoader()->client()->download(m_handle.get(), request(), m_handle.get()->firstRequest(), r); 269 // It might have gone missing 270 if (frameLoader()) 271 receivedError(interruptionForPolicyChangeError()); 272 return; 273 274 case PolicyIgnore: 275 InspectorInstrumentation::continueWithPolicyIgnore(m_frame.get(), documentLoader(), identifier(), r); 276 stopLoadingForPolicyChange(); 277 return; 278 279 default: 280 ASSERT_NOT_REACHED(); 281 } 282 283 RefPtr<MainResourceLoader> protect(this); 284 285 if (r.isHTTP()) { 286 int status = r.httpStatusCode(); 287 if (status < 200 || status >= 300) { 288 bool hostedByObject = frameLoader()->isHostedByObjectElement(); 289 290 frameLoader()->handleFallbackContent(); 291 // object elements are no longer rendered after we fallback, so don't 292 // keep trying to process data from their load 293 294 if (hostedByObject) 295 cancel(); 296 } 297 } 298 299 // we may have cancelled this load as part of switching to fallback content 300 if (!reachedTerminalState()) 301 ResourceLoader::didReceiveResponse(r); 302 303 if (frameLoader() && !frameLoader()->isStopping()) { 304 if (m_substituteData.isValid()) { 305 if (m_substituteData.content()->size()) 306 didReceiveData(m_substituteData.content()->data(), m_substituteData.content()->size(), m_substituteData.content()->size(), true); 307 if (frameLoader() && !frameLoader()->isStopping()) 308 didFinishLoading(0); 309 } else if (shouldLoadAsEmptyDocument(url) || frameLoader()->representationExistsForURLScheme(url.protocol())) 310 didFinishLoading(0); 311 } 312 } 313 314 void MainResourceLoader::callContinueAfterContentPolicy(void* argument, PolicyAction policy) 315 { 316 static_cast<MainResourceLoader*>(argument)->continueAfterContentPolicy(policy); 317 } 318 319 void MainResourceLoader::continueAfterContentPolicy(PolicyAction policy) 320 { 321 ASSERT(m_waitingForContentPolicy); 322 m_waitingForContentPolicy = false; 323 if (frameLoader() && !frameLoader()->isStopping()) 324 continueAfterContentPolicy(policy, m_response); 325 deref(); // balances ref in didReceiveResponse 326 } 327 328 #if PLATFORM(QT) 329 void MainResourceLoader::substituteMIMETypeFromPluginDatabase(const ResourceResponse& r) 330 { 331 if (!m_frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin)) 332 return; 333 334 String filename = r.url().lastPathComponent(); 335 if (filename.endsWith("/")) 336 return; 337 338 size_t extensionPos = filename.reverseFind('.'); 339 if (extensionPos == notFound) 340 return; 341 342 String extension = filename.substring(extensionPos + 1); 343 String mimeType = PluginDatabase::installedPlugins()->MIMETypeForExtension(extension); 344 if (!mimeType.isEmpty()) { 345 ResourceResponse* response = const_cast<ResourceResponse*>(&r); 346 response->setMimeType(mimeType); 347 } 348 } 349 #endif 350 351 void MainResourceLoader::didReceiveResponse(const ResourceResponse& r) 352 { 353 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 354 if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainResponse(request(), r)) 355 return; 356 #endif 357 358 HTTPHeaderMap::const_iterator it = r.httpHeaderFields().find(AtomicString("x-frame-options")); 359 if (it != r.httpHeaderFields().end()) { 360 String content = it->second; 361 if (m_frame->loader()->shouldInterruptLoadForXFrameOptions(content, r.url())) { 362 InspectorInstrumentation::continueAfterXFrameOptionsDenied(m_frame.get(), documentLoader(), identifier(), r); 363 DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to display document because display forbidden by X-Frame-Options.\n")); 364 m_frame->domWindow()->console()->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, consoleMessage, 1, String()); 365 366 cancel(); 367 return; 368 } 369 } 370 371 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. 372 // See <rdar://problem/6304600> for more details. 373 #if !USE(CF) 374 ASSERT(shouldLoadAsEmptyDocument(r.url()) || !defersLoading()); 375 #endif 376 377 #if PLATFORM(QT) 378 if (r.mimeType() == "application/octet-stream") 379 substituteMIMETypeFromPluginDatabase(r); 380 #endif 381 382 if (m_loadingMultipartContent) { 383 frameLoader()->setupForReplaceByMIMEType(r.mimeType()); 384 clearResourceData(); 385 } 386 387 if (r.isMultipart()) 388 m_loadingMultipartContent = true; 389 390 // The additional processing can do anything including possibly removing the last 391 // reference to this object; one example of this is 3266216. 392 RefPtr<MainResourceLoader> protect(this); 393 394 m_documentLoader->setResponse(r); 395 396 m_response = r; 397 398 ASSERT(!m_waitingForContentPolicy); 399 m_waitingForContentPolicy = true; 400 ref(); // balanced by deref in continueAfterContentPolicy and didCancel 401 402 ASSERT(frameLoader()->activeDocumentLoader()); 403 404 // Always show content with valid substitute data. 405 if (frameLoader()->activeDocumentLoader()->substituteData().isValid()) { 406 callContinueAfterContentPolicy(this, PolicyUse); 407 return; 408 } 409 410 #if ENABLE(FTPDIR) 411 // Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it 412 Settings* settings = m_frame->settings(); 413 if (settings && settings->forceFTPDirectoryListings() && m_response.mimeType() == "application/x-ftp-directory") { 414 callContinueAfterContentPolicy(this, PolicyUse); 415 return; 416 } 417 #endif 418 419 frameLoader()->policyChecker()->checkContentPolicy(m_response, callContinueAfterContentPolicy, this); 420 } 421 422 void MainResourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce) 423 { 424 ASSERT(data); 425 ASSERT(length != 0); 426 427 ASSERT(!m_response.isNull()); 428 429 #if USE(CFNETWORK) || (PLATFORM(MAC) && !defined(BUILDING_ON_TIGER)) 430 // Workaround for <rdar://problem/6060782> 431 if (m_response.isNull()) { 432 m_response = ResourceResponse(KURL(), "text/html", 0, String(), String()); 433 if (DocumentLoader* documentLoader = frameLoader()->activeDocumentLoader()) 434 documentLoader->setResponse(m_response); 435 } 436 #endif 437 438 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. 439 // See <rdar://problem/6304600> for more details. 440 #if !USE(CF) 441 ASSERT(!defersLoading()); 442 #endif 443 444 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 445 documentLoader()->applicationCacheHost()->mainResourceDataReceived(data, length, encodedDataLength, allAtOnce); 446 #endif 447 448 // The additional processing can do anything including possibly removing the last 449 // reference to this object; one example of this is 3266216. 450 RefPtr<MainResourceLoader> protect(this); 451 452 m_timeOfLastDataReceived = currentTime(); 453 454 ResourceLoader::didReceiveData(data, length, encodedDataLength, allAtOnce); 455 } 456 457 void MainResourceLoader::didFinishLoading(double finishTime) 458 { 459 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. 460 // See <rdar://problem/6304600> for more details. 461 #if !USE(CF) 462 ASSERT(shouldLoadAsEmptyDocument(frameLoader()->activeDocumentLoader()->url()) || !defersLoading()); 463 #endif 464 465 // The additional processing can do anything including possibly removing the last 466 // reference to this object. 467 RefPtr<MainResourceLoader> protect(this); 468 469 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 470 RefPtr<DocumentLoader> dl = documentLoader(); 471 #endif 472 473 ASSERT(!documentLoader()->timing()->responseEnd); 474 documentLoader()->timing()->responseEnd = finishTime ? finishTime : (m_timeOfLastDataReceived ? m_timeOfLastDataReceived : currentTime()); 475 frameLoader()->finishedLoading(); 476 ResourceLoader::didFinishLoading(finishTime); 477 478 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 479 dl->applicationCacheHost()->finishedLoadingMainResource(); 480 #endif 481 } 482 483 void MainResourceLoader::didFail(const ResourceError& error) 484 { 485 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 486 if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainError(request(), error)) 487 return; 488 #endif 489 490 // There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred. 491 // See <rdar://problem/6304600> for more details. 492 #if !USE(CF) 493 ASSERT(!defersLoading()); 494 #endif 495 496 receivedError(error); 497 } 498 499 void MainResourceLoader::handleEmptyLoad(const KURL& url, bool forURLScheme) 500 { 501 String mimeType; 502 if (forURLScheme) 503 mimeType = frameLoader()->generatedMIMETypeForURLScheme(url.protocol()); 504 else 505 mimeType = "text/html"; 506 507 ResourceResponse response(url, mimeType, 0, String(), String()); 508 didReceiveResponse(response); 509 } 510 511 void MainResourceLoader::handleDataLoadNow(MainResourceLoaderTimer*) 512 { 513 RefPtr<MainResourceLoader> protect(this); 514 515 KURL url = m_substituteData.responseURL(); 516 if (url.isEmpty()) 517 url = m_initialRequest.url(); 518 519 // Clear the initial request here so that subsequent entries into the 520 // loader will not think there's still a deferred load left to do. 521 m_initialRequest = ResourceRequest(); 522 523 ResourceResponse response(url, m_substituteData.mimeType(), m_substituteData.content()->size(), m_substituteData.textEncoding(), ""); 524 didReceiveResponse(response); 525 } 526 527 void MainResourceLoader::startDataLoadTimer() 528 { 529 m_dataLoadTimer.startOneShot(0); 530 531 #if HAVE(RUNLOOP_TIMER) 532 if (SchedulePairHashSet* scheduledPairs = m_frame->page()->scheduledRunLoopPairs()) 533 m_dataLoadTimer.schedule(*scheduledPairs); 534 #endif 535 } 536 537 void MainResourceLoader::handleDataLoadSoon(const ResourceRequest& r) 538 { 539 m_initialRequest = r; 540 541 if (m_documentLoader->deferMainResourceDataLoad()) 542 startDataLoadTimer(); 543 else 544 handleDataLoadNow(0); 545 } 546 547 bool MainResourceLoader::loadNow(ResourceRequest& r) 548 { 549 bool shouldLoadEmptyBeforeRedirect = shouldLoadAsEmptyDocument(r.url()); 550 551 ASSERT(!m_handle); 552 ASSERT(shouldLoadEmptyBeforeRedirect || !defersLoading()); 553 554 // Send this synthetic delegate callback since clients expect it, and 555 // we no longer send the callback from within NSURLConnection for 556 // initial requests. 557 willSendRequest(r, ResourceResponse()); 558 559 // <rdar://problem/4801066> 560 // willSendRequest() is liable to make the call to frameLoader() return NULL, so we need to check that here 561 if (!frameLoader()) 562 return false; 563 564 const KURL& url = r.url(); 565 bool shouldLoadEmpty = shouldLoadAsEmptyDocument(url) && !m_substituteData.isValid(); 566 567 if (shouldLoadEmptyBeforeRedirect && !shouldLoadEmpty && defersLoading()) 568 return true; 569 570 resourceLoadScheduler()->addMainResourceLoad(this); 571 if (m_substituteData.isValid()) 572 handleDataLoadSoon(r); 573 else if (shouldLoadEmpty || frameLoader()->representationExistsForURLScheme(url.protocol())) 574 handleEmptyLoad(url, !shouldLoadEmpty); 575 else 576 m_handle = ResourceHandle::create(m_frame->loader()->networkingContext(), r, this, false, true); 577 578 return false; 579 } 580 581 bool MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData) 582 { 583 ASSERT(!m_handle); 584 585 m_substituteData = substituteData; 586 587 ASSERT(documentLoader()->timing()->navigationStart); 588 ASSERT(!documentLoader()->timing()->fetchStart); 589 documentLoader()->timing()->fetchStart = currentTime(); 590 ResourceRequest request(r); 591 592 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 593 documentLoader()->applicationCacheHost()->maybeLoadMainResource(request, m_substituteData); 594 #endif 595 596 bool defer = defersLoading(); 597 if (defer) { 598 bool shouldLoadEmpty = shouldLoadAsEmptyDocument(request.url()); 599 if (shouldLoadEmpty) 600 defer = false; 601 } 602 if (!defer) { 603 if (loadNow(request)) { 604 // Started as an empty document, but was redirected to something non-empty. 605 ASSERT(defersLoading()); 606 defer = true; 607 } 608 } 609 if (defer) 610 m_initialRequest = request; 611 612 return true; 613 } 614 615 void MainResourceLoader::setDefersLoading(bool defers) 616 { 617 ResourceLoader::setDefersLoading(defers); 618 619 if (defers) { 620 if (m_dataLoadTimer.isActive()) 621 m_dataLoadTimer.stop(); 622 } else { 623 if (m_initialRequest.isNull()) 624 return; 625 626 if (m_substituteData.isValid() && m_documentLoader->deferMainResourceDataLoad()) 627 startDataLoadTimer(); 628 else { 629 ResourceRequest r(m_initialRequest); 630 m_initialRequest = ResourceRequest(); 631 loadNow(r); 632 } 633 } 634 } 635 636 } 637