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