1 /* 2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "config.h" 30 #include "DocumentLoader.h" 31 32 #include "ApplicationCacheHost.h" 33 #include "ArchiveResourceCollection.h" 34 #include "CachedPage.h" 35 #include "CachedResourceLoader.h" 36 #include "DOMWindow.h" 37 #include "Document.h" 38 #include "DocumentParser.h" 39 #include "DocumentWriter.h" 40 #include "Event.h" 41 #include "Frame.h" 42 #include "FrameLoader.h" 43 #include "FrameLoaderClient.h" 44 #include "FrameTree.h" 45 #include "HistoryItem.h" 46 #include "Logging.h" 47 #include "MainResourceLoader.h" 48 #include "Page.h" 49 #include "PlatformString.h" 50 #include "Settings.h" 51 #include "SharedBuffer.h" 52 #include "TextResourceDecoder.h" 53 #include <wtf/Assertions.h> 54 #include <wtf/text/CString.h> 55 #include <wtf/unicode/Unicode.h> 56 57 #if ENABLE(WEB_ARCHIVE) 58 #include "ArchiveFactory.h" 59 #endif 60 61 namespace WebCore { 62 63 static void cancelAll(const ResourceLoaderSet& loaders) 64 { 65 Vector<RefPtr<ResourceLoader> > loadersCopy; 66 copyToVector(loaders, loadersCopy); 67 size_t size = loadersCopy.size(); 68 for (size_t i = 0; i < size; ++i) 69 loadersCopy[i]->cancel(); 70 } 71 72 static void setAllDefersLoading(const ResourceLoaderSet& loaders, bool defers) 73 { 74 Vector<RefPtr<ResourceLoader> > loadersCopy; 75 copyToVector(loaders, loadersCopy); 76 size_t size = loadersCopy.size(); 77 for (size_t i = 0; i < size; ++i) 78 loadersCopy[i]->setDefersLoading(defers); 79 } 80 81 DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData& substituteData) 82 : m_deferMainResourceDataLoad(true) 83 , m_frame(0) 84 , m_writer(m_frame) 85 , m_originalRequest(req) 86 , m_substituteData(substituteData) 87 , m_originalRequestCopy(req) 88 , m_request(req) 89 , m_committed(false) 90 , m_isStopping(false) 91 , m_loading(false) 92 , m_gotFirstByte(false) 93 , m_primaryLoadComplete(false) 94 , m_isClientRedirect(false) 95 , m_wasOnloadHandled(false) 96 , m_stopRecordingResponses(false) 97 , m_substituteResourceDeliveryTimer(this, &DocumentLoader::substituteResourceDeliveryTimerFired) 98 , m_didCreateGlobalHistoryEntry(false) 99 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 100 , m_applicationCacheHost(adoptPtr(new ApplicationCacheHost(this))) 101 #endif 102 { 103 } 104 105 FrameLoader* DocumentLoader::frameLoader() const 106 { 107 if (!m_frame) 108 return 0; 109 return m_frame->loader(); 110 } 111 112 DocumentLoader::~DocumentLoader() 113 { 114 ASSERT(!m_frame || frameLoader()->activeDocumentLoader() != this || !frameLoader()->isLoading()); 115 if (m_iconLoadDecisionCallback) 116 m_iconLoadDecisionCallback->invalidate(); 117 if (m_iconDataCallback) 118 m_iconDataCallback->invalidate(); 119 } 120 121 PassRefPtr<SharedBuffer> DocumentLoader::mainResourceData() const 122 { 123 if (m_mainResourceData) 124 return m_mainResourceData; 125 if (m_mainResourceLoader) 126 return m_mainResourceLoader->resourceData(); 127 return 0; 128 } 129 130 const ResourceRequest& DocumentLoader::originalRequest() const 131 { 132 return m_originalRequest; 133 } 134 135 const ResourceRequest& DocumentLoader::originalRequestCopy() const 136 { 137 return m_originalRequestCopy; 138 } 139 140 const ResourceRequest& DocumentLoader::request() const 141 { 142 return m_request; 143 } 144 145 ResourceRequest& DocumentLoader::request() 146 { 147 return m_request; 148 } 149 150 const KURL& DocumentLoader::url() const 151 { 152 return request().url(); 153 } 154 155 void DocumentLoader::replaceRequestURLForSameDocumentNavigation(const KURL& url) 156 { 157 m_originalRequestCopy.setURL(url); 158 m_request.setURL(url); 159 } 160 161 void DocumentLoader::setRequest(const ResourceRequest& req) 162 { 163 // Replacing an unreachable URL with alternate content looks like a server-side 164 // redirect at this point, but we can replace a committed dataSource. 165 bool handlingUnreachableURL = false; 166 167 handlingUnreachableURL = m_substituteData.isValid() && !m_substituteData.failingURL().isEmpty(); 168 169 if (handlingUnreachableURL) 170 m_committed = false; 171 172 // We should never be getting a redirect callback after the data 173 // source is committed, except in the unreachable URL case. It 174 // would be a WebFoundation bug if it sent a redirect callback after commit. 175 ASSERT(!m_committed); 176 177 KURL oldURL = m_request.url(); 178 m_request = req; 179 180 // Only send webView:didReceiveServerRedirectForProvisionalLoadForFrame: if URL changed (and is non-null). 181 // Also, don't send it when replacing unreachable URLs with alternate content. 182 if (!handlingUnreachableURL && !req.url().isNull() && oldURL != req.url()) 183 frameLoader()->didReceiveServerRedirectForProvisionalLoadForFrame(); 184 } 185 186 void DocumentLoader::setMainDocumentError(const ResourceError& error) 187 { 188 m_mainDocumentError = error; 189 frameLoader()->setMainDocumentError(this, error); 190 } 191 192 void DocumentLoader::clearErrors() 193 { 194 m_mainDocumentError = ResourceError(); 195 } 196 197 void DocumentLoader::mainReceivedError(const ResourceError& error, bool isComplete) 198 { 199 ASSERT(!error.isNull()); 200 201 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 202 m_applicationCacheHost->failedLoadingMainResource(); 203 #endif 204 205 if (!frameLoader()) 206 return; 207 setMainDocumentError(error); 208 if (isComplete) 209 frameLoader()->mainReceivedCompleteError(this, error); 210 } 211 212 // Cancels the data source's pending loads. Conceptually, a data source only loads 213 // one document at a time, but one document may have many related resources. 214 // stopLoading will stop all loads initiated by the data source, 215 // but not loads initiated by child frames' data sources -- that's the WebFrame's job. 216 void DocumentLoader::stopLoading() 217 { 218 // In some rare cases, calling FrameLoader::stopLoading could set m_loading to false. 219 // (This can happen when there's a single XMLHttpRequest currently loading and stopLoading causes it 220 // to stop loading. Because of this, we need to save it so we don't return early. 221 bool loading = m_loading; 222 223 if (m_committed) { 224 // Attempt to stop the frame if the document loader is loading, or if it is done loading but 225 // still parsing. Failure to do so can cause a world leak. 226 Document* doc = m_frame->document(); 227 228 if (loading || doc->parsing()) 229 m_frame->loader()->stopLoading(UnloadEventPolicyNone); 230 } 231 232 // Always cancel multipart loaders 233 cancelAll(m_multipartSubresourceLoaders); 234 235 // Appcache uses ResourceHandle directly, DocumentLoader doesn't count these loads. 236 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 237 m_applicationCacheHost->stopLoadingInFrame(m_frame); 238 #endif 239 240 if (!loading) 241 return; 242 243 RefPtr<Frame> protectFrame(m_frame); 244 RefPtr<DocumentLoader> protectLoader(this); 245 246 m_isStopping = true; 247 248 FrameLoader* frameLoader = DocumentLoader::frameLoader(); 249 250 if (m_mainResourceLoader) 251 // Stop the main resource loader and let it send the cancelled message. 252 m_mainResourceLoader->cancel(); 253 else if (!m_subresourceLoaders.isEmpty()) 254 // The main resource loader already finished loading. Set the cancelled error on the 255 // document and let the subresourceLoaders send individual cancelled messages below. 256 setMainDocumentError(frameLoader->cancelledError(m_request)); 257 else 258 // If there are no resource loaders, we need to manufacture a cancelled message. 259 // (A back/forward navigation has no resource loaders because its resources are cached.) 260 mainReceivedError(frameLoader->cancelledError(m_request), true); 261 262 stopLoadingSubresources(); 263 stopLoadingPlugIns(); 264 265 m_isStopping = false; 266 } 267 268 void DocumentLoader::setupForReplace() 269 { 270 frameLoader()->setupForReplace(); 271 m_committed = false; 272 } 273 274 void DocumentLoader::commitIfReady() 275 { 276 if (m_gotFirstByte && !m_committed) { 277 m_committed = true; 278 frameLoader()->commitProvisionalLoad(); 279 } 280 } 281 282 void DocumentLoader::finishedLoading() 283 { 284 m_gotFirstByte = true; 285 commitIfReady(); 286 if (FrameLoader* loader = frameLoader()) { 287 loader->finishedLoadingDocument(this); 288 m_writer.end(); 289 } 290 } 291 292 void DocumentLoader::commitLoad(const char* data, int length) 293 { 294 // Both unloading the old page and parsing the new page may execute JavaScript which destroys the datasource 295 // by starting a new load, so retain temporarily. 296 RefPtr<Frame> protectFrame(m_frame); 297 RefPtr<DocumentLoader> protectLoader(this); 298 299 commitIfReady(); 300 FrameLoader* frameLoader = DocumentLoader::frameLoader(); 301 if (!frameLoader) 302 return; 303 #if ENABLE(WEB_ARCHIVE) 304 if (ArchiveFactory::isArchiveMimeType(response().mimeType())) 305 return; 306 #endif 307 frameLoader->client()->committedLoad(this, data, length); 308 } 309 310 void DocumentLoader::commitData(const char* bytes, int length) 311 { 312 // Set the text encoding. This is safe to call multiple times. 313 bool userChosen = true; 314 String encoding = overrideEncoding(); 315 if (encoding.isNull()) { 316 userChosen = false; 317 encoding = response().textEncodingName(); 318 } 319 m_writer.setEncoding(encoding, userChosen); 320 ASSERT(m_frame->document()->parsing()); 321 m_writer.addData(bytes, length); 322 } 323 324 bool DocumentLoader::doesProgressiveLoad(const String& MIMEType) const 325 { 326 return !frameLoader()->isReplacing() || MIMEType == "text/html"; 327 } 328 329 void DocumentLoader::receivedData(const char* data, int length) 330 { 331 m_gotFirstByte = true; 332 if (doesProgressiveLoad(m_response.mimeType())) 333 commitLoad(data, length); 334 } 335 336 void DocumentLoader::setupForReplaceByMIMEType(const String& newMIMEType) 337 { 338 if (!m_gotFirstByte) 339 return; 340 341 String oldMIMEType = m_response.mimeType(); 342 343 if (!doesProgressiveLoad(oldMIMEType)) { 344 frameLoader()->revertToProvisional(this); 345 setupForReplace(); 346 RefPtr<SharedBuffer> resourceData = mainResourceData(); 347 commitLoad(resourceData->data(), resourceData->size()); 348 } 349 350 frameLoader()->finishedLoadingDocument(this); 351 m_writer.end(); 352 353 frameLoader()->setReplacing(); 354 m_gotFirstByte = false; 355 356 if (doesProgressiveLoad(newMIMEType)) { 357 frameLoader()->revertToProvisional(this); 358 setupForReplace(); 359 } 360 361 stopLoadingSubresources(); 362 stopLoadingPlugIns(); 363 #if ENABLE(WEB_ARCHIVE) 364 clearArchiveResources(); 365 #endif 366 } 367 368 void DocumentLoader::updateLoading() 369 { 370 if (!m_frame) { 371 setLoading(false); 372 return; 373 } 374 ASSERT(this == frameLoader()->activeDocumentLoader()); 375 bool wasLoading = m_loading; 376 setLoading(frameLoader()->isLoading()); 377 378 if (wasLoading && !m_loading) { 379 if (DOMWindow* window = m_frame->existingDOMWindow()) 380 window->finishedLoading(); 381 } 382 } 383 384 void DocumentLoader::setFrame(Frame* frame) 385 { 386 if (m_frame == frame) 387 return; 388 ASSERT(frame && !m_frame); 389 m_frame = frame; 390 m_writer.setFrame(frame); 391 attachToFrame(); 392 } 393 394 void DocumentLoader::attachToFrame() 395 { 396 ASSERT(m_frame); 397 } 398 399 void DocumentLoader::detachFromFrame() 400 { 401 ASSERT(m_frame); 402 #if ENABLE(OFFLINE_WEB_APPLICATIONS) 403 m_applicationCacheHost->setDOMApplicationCache(0); 404 #endif 405 m_frame = 0; 406 } 407 408 void DocumentLoader::prepareForLoadStart() 409 { 410 ASSERT(!m_isStopping); 411 setPrimaryLoadComplete(false); 412 ASSERT(frameLoader()); 413 clearErrors(); 414 415 setLoading(true); 416 417 frameLoader()->prepareForLoadStart(); 418 } 419 420 void DocumentLoader::setPrimaryLoadComplete(bool flag) 421 { 422 m_primaryLoadComplete = flag; 423 if (flag) { 424 if (m_mainResourceLoader) { 425 m_mainResourceData = m_mainResourceLoader->resourceData(); 426 m_mainResourceLoader = 0; 427 } 428 429 if (this == frameLoader()->activeDocumentLoader()) 430 updateLoading(); 431 } 432 } 433 434 bool DocumentLoader::isLoadingInAPISense() const 435 { 436 // Once a frame has loaded, we no longer need to consider subresources, 437 // but we still need to consider subframes. 438 if (frameLoader()->state() != FrameStateComplete) { 439 if (!m_primaryLoadComplete && isLoading()) 440 return true; 441 if (!m_subresourceLoaders.isEmpty()) 442 return true; 443 Document* doc = m_frame->document(); 444 if (doc->cachedResourceLoader()->requestCount()) 445 return true; 446 if (DocumentParser* parser = doc->parser()) 447 if (parser->processingData()) 448 return true; 449 } 450 return frameLoader()->subframeIsLoading(); 451 } 452 453 #if ENABLE(WEB_ARCHIVE) 454 void DocumentLoader::addAllArchiveResources(Archive* archive) 455 { 456 if (!m_archiveResourceCollection) 457 m_archiveResourceCollection = adoptPtr(new ArchiveResourceCollection); 458 459 ASSERT(archive); 460 if (!archive) 461 return; 462 463 m_archiveResourceCollection->addAllResources(archive); 464 } 465 466 // FIXME: Adding a resource directly to a DocumentLoader/ArchiveResourceCollection seems like bad design, but is API some apps rely on. 467 // Can we change the design in a manner that will let us deprecate that API without reducing functionality of those apps? 468 void DocumentLoader::addArchiveResource(PassRefPtr<ArchiveResource> resource) 469 { 470 if (!m_archiveResourceCollection) 471 m_archiveResourceCollection = adoptPtr(new ArchiveResourceCollection); 472 473 ASSERT(resource); 474 if (!resource) 475 return; 476 477 m_archiveResourceCollection->addResource(resource); 478 } 479 480 PassRefPtr<Archive> DocumentLoader::popArchiveForSubframe(const String& frameName) 481 { 482 return m_archiveResourceCollection ? m_archiveResourceCollection->popSubframeArchive(frameName) : 0; 483 } 484 485 void DocumentLoader::clearArchiveResources() 486 { 487 m_archiveResourceCollection.clear(); 488 m_substituteResourceDeliveryTimer.stop(); 489 } 490 491 void DocumentLoader::setParsedArchiveData(PassRefPtr<SharedBuffer> data) 492 { 493 m_parsedArchiveData = data; 494 } 495 496 SharedBuffer* DocumentLoader::parsedArchiveData() const 497 { 498 return m_parsedArchiveData.get(); 499 } 500 #endif // ENABLE(WEB_ARCHIVE) 501 502 ArchiveResource* DocumentLoader::archiveResourceForURL(const KURL& url) const 503 { 504 if (!m_archiveResourceCollection) 505 return 0; 506 507 ArchiveResource* resource = m_archiveResourceCollection->archiveResourceForURL(url); 508 509 return resource && !resource->shouldIgnoreWhenUnarchiving() ? resource : 0; 510 } 511 512 PassRefPtr<ArchiveResource> DocumentLoader::mainResource() const 513 { 514 const ResourceResponse& r = response(); 515 RefPtr<SharedBuffer> mainResourceBuffer = mainResourceData(); 516 if (!mainResourceBuffer) 517 mainResourceBuffer = SharedBuffer::create(); 518 519 return ArchiveResource::create(mainResourceBuffer, r.url(), r.mimeType(), r.textEncodingName(), frame()->tree()->uniqueName()); 520 } 521 522 PassRefPtr<ArchiveResource> DocumentLoader::subresource(const KURL& url) const 523 { 524 if (!isCommitted()) 525 return 0; 526 527 CachedResource* resource = m_frame->document()->cachedResourceLoader()->cachedResource(url); 528 if (!resource || !resource->isLoaded()) 529 return archiveResourceForURL(url); 530 531 // FIXME: This has the side effect of making the resource non-purgeable. 532 // It would be better if it didn't have this permanent effect. 533 if (!resource->makePurgeable(false)) 534 return 0; 535 536 RefPtr<SharedBuffer> data = resource->data(); 537 if (!data) 538 return 0; 539 540 return ArchiveResource::create(data.release(), url, resource->response()); 541 } 542 543 void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subresources) const 544 { 545 if (!isCommitted()) 546 return; 547 548 Document* document = m_frame->document(); 549 550 const CachedResourceLoader::DocumentResourceMap& allResources = document->cachedResourceLoader()->allCachedResources(); 551 CachedResourceLoader::DocumentResourceMap::const_iterator end = allResources.end(); 552 for (CachedResourceLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) { 553 RefPtr<ArchiveResource> subresource = this->subresource(KURL(ParsedURLString, it->second->url())); 554 if (subresource) 555 subresources.append(subresource.release()); 556 } 557 558 return; 559 } 560 561 void DocumentLoader::deliverSubstituteResourcesAfterDelay() 562 { 563 if (m_pendingSubstituteResources.isEmpty()) 564 return; 565 ASSERT(m_frame && m_frame->page()); 566 if (m_frame->page()->defersLoading()) 567 return; 568 if (!m_substituteResourceDeliveryTimer.isActive()) 569 m_substituteResourceDeliveryTimer.startOneShot(0); 570 } 571 572 void DocumentLoader::substituteResourceDeliveryTimerFired(Timer<DocumentLoader>*) 573 { 574 if (m_pendingSubstituteResources.isEmpty()) 575 return; 576 ASSERT(m_frame && m_frame->page()); 577 if (m_frame->page()->defersLoading()) 578 return; 579 580 SubstituteResourceMap copy; 581 copy.swap(m_pendingSubstituteResources); 582 583 SubstituteResourceMap::const_iterator end = copy.end(); 584 for (SubstituteResourceMap::const_iterator it = copy.begin(); it != end; ++it) { 585 RefPtr<ResourceLoader> loader = it->first; 586 SubstituteResource* resource = it->second.get(); 587 588 if (resource) { 589 SharedBuffer* data = resource->data(); 590 591 loader->didReceiveResponse(resource->response()); 592 loader->didReceiveData(data->data(), data->size(), data->size(), true); 593 loader->didFinishLoading(0); 594 } else { 595 // A null resource means that we should fail the load. 596 // FIXME: Maybe we should use another error here - something like "not in cache". 597 loader->didFail(loader->cannotShowURLError()); 598 } 599 } 600 } 601 602 #ifndef NDEBUG 603 bool DocumentLoader::isSubstituteLoadPending(ResourceLoader* loader) const 604 { 605 return m_pendingSubstituteResources.contains(loader); 606 } 607 #endif 608 609 void DocumentLoader::cancelPendingSubstituteLoad(ResourceLoader* loader) 610 { 611 if (m_pendingSubstituteResources.isEmpty()) 612 return; 613 m_pendingSubstituteResources.remove(loader); 614 if (m_pendingSubstituteResources.isEmpty()) 615 m_substituteResourceDeliveryTimer.stop(); 616 } 617 618 #if ENABLE(WEB_ARCHIVE) 619 bool DocumentLoader::scheduleArchiveLoad(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL) 620 { 621 ArchiveResource* resource = 0; 622 623 if (request.url() == originalURL) 624 resource = archiveResourceForURL(originalURL); 625 626 if (!resource) { 627 // WebArchiveDebugMode means we fail loads instead of trying to fetch them from the network if they're not in the archive. 628 bool shouldFailLoad = m_frame->settings()->webArchiveDebugModeEnabled() && ArchiveFactory::isArchiveMimeType(responseMIMEType()); 629 630 if (!shouldFailLoad) 631 return false; 632 } 633 634 m_pendingSubstituteResources.set(loader, resource); 635 deliverSubstituteResourcesAfterDelay(); 636 637 return true; 638 } 639 #endif // ENABLE(WEB_ARCHIVE) 640 641 void DocumentLoader::addResponse(const ResourceResponse& r) 642 { 643 if (!m_stopRecordingResponses) 644 m_responses.append(r); 645 } 646 647 void DocumentLoader::stopRecordingResponses() 648 { 649 m_stopRecordingResponses = true; 650 } 651 652 void DocumentLoader::setTitle(const StringWithDirection& title) 653 { 654 if (title.isEmpty()) 655 return; 656 657 if (m_pageTitle != title) { 658 frameLoader()->willChangeTitle(this); 659 m_pageTitle = title; 660 frameLoader()->didChangeTitle(this); 661 } 662 } 663 664 void DocumentLoader::setIconURL(const String& iconURL) 665 { 666 if (iconURL.isEmpty()) 667 return; 668 669 if (m_pageIconURL != iconURL) { 670 m_pageIconURL = iconURL; 671 frameLoader()->didChangeIcons(this); 672 } 673 } 674 675 KURL DocumentLoader::urlForHistory() const 676 { 677 // Return the URL to be used for history and B/F list. 678 // Returns nil for WebDataProtocol URLs that aren't alternates 679 // for unreachable URLs, because these can't be stored in history. 680 if (m_substituteData.isValid()) 681 return unreachableURL(); 682 683 return m_originalRequestCopy.url(); 684 } 685 686 bool DocumentLoader::urlForHistoryReflectsFailure() const 687 { 688 return m_substituteData.isValid() || m_response.httpStatusCode() >= 400; 689 } 690 691 const KURL& DocumentLoader::originalURL() const 692 { 693 return m_originalRequestCopy.url(); 694 } 695 696 const KURL& DocumentLoader::requestURL() const 697 { 698 return request().url(); 699 } 700 701 const KURL& DocumentLoader::responseURL() const 702 { 703 return m_response.url(); 704 } 705 706 const String& DocumentLoader::responseMIMEType() const 707 { 708 return m_response.mimeType(); 709 } 710 711 const KURL& DocumentLoader::unreachableURL() const 712 { 713 return m_substituteData.failingURL(); 714 } 715 716 void DocumentLoader::setDefersLoading(bool defers) 717 { 718 if (m_mainResourceLoader) 719 m_mainResourceLoader->setDefersLoading(defers); 720 setAllDefersLoading(m_subresourceLoaders, defers); 721 setAllDefersLoading(m_plugInStreamLoaders, defers); 722 if (!defers) 723 deliverSubstituteResourcesAfterDelay(); 724 } 725 726 void DocumentLoader::stopLoadingPlugIns() 727 { 728 cancelAll(m_plugInStreamLoaders); 729 } 730 731 void DocumentLoader::stopLoadingSubresources() 732 { 733 cancelAll(m_subresourceLoaders); 734 } 735 736 void DocumentLoader::addSubresourceLoader(ResourceLoader* loader) 737 { 738 m_subresourceLoaders.add(loader); 739 setLoading(true); 740 } 741 742 void DocumentLoader::removeSubresourceLoader(ResourceLoader* loader) 743 { 744 m_subresourceLoaders.remove(loader); 745 updateLoading(); 746 if (Frame* frame = m_frame) 747 frame->loader()->checkLoadComplete(); 748 } 749 750 void DocumentLoader::addPlugInStreamLoader(ResourceLoader* loader) 751 { 752 m_plugInStreamLoaders.add(loader); 753 setLoading(true); 754 } 755 756 void DocumentLoader::removePlugInStreamLoader(ResourceLoader* loader) 757 { 758 m_plugInStreamLoaders.remove(loader); 759 updateLoading(); 760 } 761 762 bool DocumentLoader::isLoadingMainResource() const 763 { 764 return !!m_mainResourceLoader; 765 } 766 767 bool DocumentLoader::isLoadingSubresources() const 768 { 769 return !m_subresourceLoaders.isEmpty(); 770 } 771 772 bool DocumentLoader::isLoadingPlugIns() const 773 { 774 return !m_plugInStreamLoaders.isEmpty(); 775 } 776 777 bool DocumentLoader::isLoadingMultipartContent() const 778 { 779 return m_mainResourceLoader && m_mainResourceLoader->isLoadingMultipartContent(); 780 } 781 782 bool DocumentLoader::startLoadingMainResource(unsigned long identifier) 783 { 784 ASSERT(!m_mainResourceLoader); 785 m_mainResourceLoader = MainResourceLoader::create(m_frame); 786 m_mainResourceLoader->setIdentifier(identifier); 787 788 // FIXME: Is there any way the extra fields could have not been added by now? 789 // If not, it would be great to remove this line of code. 790 frameLoader()->addExtraFieldsToMainResourceRequest(m_request); 791 792 if (!m_mainResourceLoader->load(m_request, m_substituteData)) { 793 // FIXME: If this should really be caught, we should just ASSERT this doesn't happen; 794 // should it be caught by other parts of WebKit or other parts of the app? 795 LOG_ERROR("could not create WebResourceHandle for URL %s -- should be caught by policy handler level", m_request.url().string().ascii().data()); 796 m_mainResourceLoader = 0; 797 return false; 798 } 799 800 return true; 801 } 802 803 void DocumentLoader::cancelMainResourceLoad(const ResourceError& error) 804 { 805 m_mainResourceLoader->cancel(error); 806 } 807 808 void DocumentLoader::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader) 809 { 810 m_multipartSubresourceLoaders.add(loader); 811 m_subresourceLoaders.remove(loader); 812 updateLoading(); 813 if (Frame* frame = m_frame) 814 frame->loader()->checkLoadComplete(); 815 } 816 817 void DocumentLoader::transferLoadingResourcesFromPage(Page* oldPage) 818 { 819 ASSERT(oldPage != m_frame->page()); 820 821 FrameLoader* loader = frameLoader(); 822 ASSERT(loader); 823 824 const ResourceRequest& request = originalRequest(); 825 if (isLoadingMainResource()) { 826 loader->dispatchTransferLoadingResourceFromPage( 827 m_mainResourceLoader->identifier(), this, request, oldPage); 828 } 829 830 if (isLoadingSubresources()) { 831 ResourceLoaderSet::const_iterator it = m_subresourceLoaders.begin(); 832 ResourceLoaderSet::const_iterator end = m_subresourceLoaders.end(); 833 for (; it != end; ++it) { 834 loader->dispatchTransferLoadingResourceFromPage( 835 (*it)->identifier(), this, request, oldPage); 836 } 837 } 838 } 839 840 void DocumentLoader::iconLoadDecisionAvailable() 841 { 842 if (m_frame) 843 m_frame->loader()->iconLoadDecisionReceived(iconDatabase().synchronousLoadDecisionForIconURL(KURL(frameLoader()->iconURL()), this)); 844 } 845 846 static void iconLoadDecisionCallback(IconLoadDecision decision, void* context) 847 { 848 static_cast<DocumentLoader*>(context)->continueIconLoadWithDecision(decision); 849 } 850 851 void DocumentLoader::getIconLoadDecisionForIconURL(const String& urlString) 852 { 853 if (m_iconLoadDecisionCallback) 854 m_iconLoadDecisionCallback->invalidate(); 855 m_iconLoadDecisionCallback = IconLoadDecisionCallback::create(this, iconLoadDecisionCallback); 856 iconDatabase().loadDecisionForIconURL(urlString, m_iconLoadDecisionCallback); 857 } 858 859 void DocumentLoader::continueIconLoadWithDecision(IconLoadDecision decision) 860 { 861 ASSERT(m_iconLoadDecisionCallback); 862 m_iconLoadDecisionCallback = 0; 863 if (m_frame) 864 m_frame->loader()->continueIconLoadWithDecision(decision); 865 } 866 867 static void iconDataCallback(SharedBuffer*, void*) 868 { 869 // FIXME: Implement this once we know what parts of WebCore actually need the icon data returned. 870 } 871 872 void DocumentLoader::getIconDataForIconURL(const String& urlString) 873 { 874 if (m_iconDataCallback) 875 m_iconDataCallback->invalidate(); 876 m_iconDataCallback = IconDataCallback::create(this, iconDataCallback); 877 iconDatabase().iconDataForIconURL(urlString, m_iconDataCallback); 878 } 879 880 } // namespace WebCore 881