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