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