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