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