00be097a0ede9dc50a5a7b8cdb480395ad7064ad
[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     Document* doc = m_frame->document();
553         
554     CachedResource* resource = doc->docLoader()->cachedResource(url);
555     if (!resource || resource->preloadResult() == CachedResource::PreloadReferenced)
556         return archiveResourceForURL(url);
557         
558     return ArchiveResource::create(resource->data(), url, resource->response());
559 }
560
561 void DocumentLoader::getSubresources(Vector<PassRefPtr<ArchiveResource> >& subresources) const
562 {
563     if (!isCommitted())
564         return;
565
566     Document* document = m_frame->document();
567
568     const DocLoader::DocumentResourceMap& allResources = document->docLoader()->allCachedResources();
569     DocLoader::DocumentResourceMap::const_iterator end = allResources.end();
570     for (DocLoader::DocumentResourceMap::const_iterator it = allResources.begin(); it != end; ++it) {
571         RefPtr<ArchiveResource> subresource = this->subresource(KURL(it->second->url()));
572         if (subresource)
573             subresources.append(subresource.release());
574     }
575
576     return;
577 }
578
579 void DocumentLoader::deliverSubstituteResourcesAfterDelay()
580 {
581     if (m_pendingSubstituteResources.isEmpty())
582         return;
583     ASSERT(m_frame && m_frame->page());
584     if (m_frame->page()->defersLoading())
585         return;
586     if (!m_substituteResourceDeliveryTimer.isActive())
587         m_substituteResourceDeliveryTimer.startOneShot(0);
588 }
589
590 void DocumentLoader::substituteResourceDeliveryTimerFired(Timer<DocumentLoader>*)
591 {
592     if (m_pendingSubstituteResources.isEmpty())
593         return;
594     ASSERT(m_frame && m_frame->page());
595     if (m_frame->page()->defersLoading())
596         return;
597
598     SubstituteResourceMap copy;
599     copy.swap(m_pendingSubstituteResources);
600
601     SubstituteResourceMap::const_iterator end = copy.end();
602     for (SubstituteResourceMap::const_iterator it = copy.begin(); it != end; ++it) {
603         RefPtr<ResourceLoader> loader = it->first;
604         SubstituteResource* resource = it->second.get();
605         
606         if (resource) {
607             SharedBuffer* data = resource->data();
608         
609             loader->didReceiveResponse(resource->response());
610             loader->didReceiveData(data->data(), data->size(), data->size(), true);
611             loader->didFinishLoading();
612         } else {
613             // A null resource means that we should fail the load.
614             // FIXME: Maybe we should use another error here - something like "not in cache".
615             loader->didFail(loader->cannotShowURLError());
616         }
617     }
618 }
619
620 #ifndef NDEBUG
621 bool DocumentLoader::isSubstituteLoadPending(ResourceLoader* loader) const
622 {
623     return m_pendingSubstituteResources.contains(loader);
624 }
625 #endif
626
627 void DocumentLoader::cancelPendingSubstituteLoad(ResourceLoader* loader)
628 {
629     if (m_pendingSubstituteResources.isEmpty())
630         return;
631     m_pendingSubstituteResources.remove(loader);
632     if (m_pendingSubstituteResources.isEmpty())
633         m_substituteResourceDeliveryTimer.stop();
634 }
635
636 bool DocumentLoader::scheduleArchiveLoad(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL)
637 {
638     ArchiveResource* resource = 0;
639     
640     if (request.url() == originalURL)
641         resource = archiveResourceForURL(originalURL);
642
643     if (!resource) {
644         // WebArchiveDebugMode means we fail loads instead of trying to fetch them from the network if they're not in the archive.
645         bool shouldFailLoad = m_frame->settings()->webArchiveDebugModeEnabled() && ArchiveFactory::isArchiveMimeType(responseMIMEType());
646
647         if (!shouldFailLoad)
648             return false;
649     }
650     
651     m_pendingSubstituteResources.set(loader, resource);
652     deliverSubstituteResourcesAfterDelay();
653     
654     return true;
655 }
656
657 void DocumentLoader::addResponse(const ResourceResponse& r)
658 {
659     if (!m_stopRecordingResponses)
660         m_responses.append(r);
661 }
662
663 void DocumentLoader::stopRecordingResponses()
664 {
665     m_stopRecordingResponses = true;
666 }
667
668 void DocumentLoader::setTitle(const String& title)
669 {
670     if (title.isEmpty())
671         return;
672
673     String trimmed = canonicalizedTitle(title, m_frame);
674     if (!trimmed.isEmpty() && m_pageTitle != trimmed) {
675         frameLoader()->willChangeTitle(this);
676         m_pageTitle = trimmed;
677         frameLoader()->didChangeTitle(this);
678     }
679 }
680
681 KURL DocumentLoader::urlForHistory() const
682 {
683     // Return the URL to be used for history and B/F list.
684     // Returns nil for WebDataProtocol URLs that aren't alternates 
685     // for unreachable URLs, because these can't be stored in history.
686     if (m_substituteData.isValid())
687         return unreachableURL();
688
689     return m_originalRequestCopy.url();
690 }
691
692 bool DocumentLoader::urlForHistoryReflectsFailure() const
693 {
694     return m_substituteData.isValid() || m_response.httpStatusCode() >= 400;
695 }
696
697 void DocumentLoader::loadFromCachedPage(PassRefPtr<CachedPage> cachedPage)
698 {
699     LOG(PageCache, "WebCorePageCache: DocumentLoader %p loading from cached page %p", this, cachedPage.get());
700     
701     prepareForLoadStart();
702     setLoadingFromCachedPage(true);
703     setCommitted(true);
704     frameLoader()->commitProvisionalLoad(cachedPage);
705 }
706
707 const KURL& DocumentLoader::originalURL() const
708 {
709     return m_originalRequestCopy.url();
710 }
711
712 const KURL& DocumentLoader::requestURL() const
713 {
714     return request().url();
715 }
716
717 const KURL& DocumentLoader::responseURL() const
718 {
719     return m_response.url();
720 }
721
722 const String& DocumentLoader::responseMIMEType() const
723 {
724     return m_response.mimeType();
725 }
726
727 const KURL& DocumentLoader::unreachableURL() const
728 {
729     return m_substituteData.failingURL();
730 }
731
732 void DocumentLoader::setDefersLoading(bool defers)
733 {
734     if (m_mainResourceLoader)
735         m_mainResourceLoader->setDefersLoading(defers);
736     setAllDefersLoading(m_subresourceLoaders, defers);
737     setAllDefersLoading(m_plugInStreamLoaders, defers);
738     if (!defers)
739         deliverSubstituteResourcesAfterDelay();
740 }
741
742 void DocumentLoader::stopLoadingPlugIns()
743 {
744     cancelAll(m_plugInStreamLoaders);
745 }
746
747 void DocumentLoader::stopLoadingSubresources()
748 {
749     cancelAll(m_subresourceLoaders);
750 }
751
752 void DocumentLoader::addSubresourceLoader(ResourceLoader* loader)
753 {
754     m_subresourceLoaders.add(loader);
755     setLoading(true);
756 }
757
758 void DocumentLoader::removeSubresourceLoader(ResourceLoader* loader)
759 {
760     m_subresourceLoaders.remove(loader);
761     updateLoading();
762     if (Frame* frame = m_frame)
763         frame->loader()->checkLoadComplete();
764 }
765
766 void DocumentLoader::addPlugInStreamLoader(ResourceLoader* loader)
767 {
768     m_plugInStreamLoaders.add(loader);
769     setLoading(true);
770 }
771
772 void DocumentLoader::removePlugInStreamLoader(ResourceLoader* loader)
773 {
774     m_plugInStreamLoaders.remove(loader);
775     updateLoading();
776 }
777
778 bool DocumentLoader::isLoadingMainResource() const
779 {
780     return !!m_mainResourceLoader;
781 }
782
783 bool DocumentLoader::isLoadingSubresources() const
784 {
785     return !m_subresourceLoaders.isEmpty();
786 }
787
788 bool DocumentLoader::isLoadingPlugIns() const
789 {
790     return !m_plugInStreamLoaders.isEmpty();
791 }
792
793 bool DocumentLoader::isLoadingMultipartContent() const
794 {
795     return m_mainResourceLoader && m_mainResourceLoader->isLoadingMultipartContent();
796 }
797
798 bool DocumentLoader::startLoadingMainResource(unsigned long identifier)
799 {
800     ASSERT(!m_mainResourceLoader);
801     m_mainResourceLoader = MainResourceLoader::create(m_frame);
802     m_mainResourceLoader->setIdentifier(identifier);
803
804     // FIXME: Is there any way the extra fields could have not been added by now?
805     // If not, it would be great to remove this line of code.
806     frameLoader()->addExtraFieldsToMainResourceRequest(m_request);
807
808     if (!m_mainResourceLoader->load(m_request, m_substituteData)) {
809         // FIXME: If this should really be caught, we should just ASSERT this doesn't happen;
810         // should it be caught by other parts of WebKit or other parts of the app?
811         LOG_ERROR("could not create WebResourceHandle for URL %s -- should be caught by policy handler level", m_request.url().string().ascii().data());
812         m_mainResourceLoader = 0;
813         return false;
814     }
815
816     return true;
817 }
818
819 void DocumentLoader::cancelMainResourceLoad(const ResourceError& error)
820 {
821     m_mainResourceLoader->cancel(error);
822 }
823
824 void DocumentLoader::subresourceLoaderFinishedLoadingOnePart(ResourceLoader* loader)
825 {
826     m_multipartSubresourceLoaders.add(loader);
827     m_subresourceLoaders.remove(loader);
828     updateLoading();
829     if (Frame* frame = m_frame)
830         frame->loader()->checkLoadComplete();    
831 }
832
833 void DocumentLoader::iconLoadDecisionAvailable()
834 {
835     if (m_frame)
836         m_frame->loader()->iconLoadDecisionAvailable();
837 }
838
839 #if ENABLE(OFFLINE_WEB_APPLICATIONS)
840 void DocumentLoader::setCandidateApplicationCacheGroup(ApplicationCacheGroup* group)
841 {
842     ASSERT(!m_applicationCache);
843     m_candidateApplicationCacheGroup = group;
844 }
845     
846 void DocumentLoader::setApplicationCache(PassRefPtr<ApplicationCache> applicationCache)
847 {
848     if (m_candidateApplicationCacheGroup) {
849         ASSERT(!m_applicationCache);
850         m_candidateApplicationCacheGroup = 0;
851     }
852
853     m_applicationCache = applicationCache;
854 }
855
856 ApplicationCache* DocumentLoader::mainResourceApplicationCache() const
857 {
858     if (m_mainResourceApplicationCache)
859         return m_mainResourceApplicationCache.get();
860     if (m_mainResourceLoader)
861         return m_mainResourceLoader->applicationCache();
862     return 0;
863 }
864
865 bool DocumentLoader::shouldLoadResourceFromApplicationCache(const ResourceRequest& request, ApplicationCacheResource*& resource)
866 {
867     ApplicationCache* cache = applicationCache();
868     if (!cache || !cache->isComplete())
869         return false;
870
871     // If the resource is not a HTTP/HTTPS GET, then abort
872     if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
873         return false;
874
875     // If the resource's URL is an master entry, the manifest, an explicit entry, a fallback entry, or a dynamic entry
876     // in the application cache, then get the resource from the cache (instead of fetching it).
877     resource = cache->resourceForURL(request.url());
878
879     // Resources that match fallback namespaces or online whitelist entries are fetched from the network,
880     // unless they are also cached.
881     if (!resource && (cache->urlMatchesFallbackNamespace(request.url()) || cache->isURLInOnlineWhitelist(request.url())))
882         return false;
883
884     // Resources that are not present in the manifest will always fail to load (at least, after the
885     // cache has been primed the first time), making the testing of offline applications simpler.
886     return true;
887 }
888
889 bool DocumentLoader::getApplicationCacheFallbackResource(const ResourceRequest& request, ApplicationCacheResource*& resource, ApplicationCache* cache)
890 {
891     if (!cache) {
892         cache = applicationCache();
893         if (!cache)
894             return false;
895     }
896     if (!cache->isComplete())
897         return false;
898     
899     // If the resource is not a HTTP/HTTPS GET, then abort
900     if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request))
901         return false;
902
903     KURL fallbackURL;
904     if (!cache->urlMatchesFallbackNamespace(request.url(), &fallbackURL))
905         return false;
906
907     resource = cache->resourceForURL(fallbackURL);
908     ASSERT(resource);
909
910     return true;
911 }
912
913 bool DocumentLoader::scheduleApplicationCacheLoad(ResourceLoader* loader, const ResourceRequest& request, const KURL& originalURL)
914 {
915     if (!frameLoader()->frame()->settings() || !frameLoader()->frame()->settings()->offlineWebApplicationCacheEnabled())
916         return false;
917     
918     if (request.url() != originalURL)
919         return false;
920
921     ApplicationCacheResource* resource;
922     if (!shouldLoadResourceFromApplicationCache(request, resource))
923         return false;
924     
925     m_pendingSubstituteResources.set(loader, resource);
926     deliverSubstituteResourcesAfterDelay();
927         
928     return true;
929 }
930
931 bool DocumentLoader::scheduleLoadFallbackResourceFromApplicationCache(ResourceLoader* loader, const ResourceRequest& request, ApplicationCache* cache)
932 {
933     if (!frameLoader()->frame()->settings() || !frameLoader()->frame()->settings()->offlineWebApplicationCacheEnabled())
934         return false;
935
936     ApplicationCacheResource* resource;
937     if (!getApplicationCacheFallbackResource(request, resource, cache))
938         return false;
939
940     m_pendingSubstituteResources.set(loader, resource);
941     deliverSubstituteResourcesAfterDelay();
942         
943     return true;
944 }
945
946 #endif // ENABLE(OFFLINE_WEB_APPLICATIONS)
947
948 }