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