2
* Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3
* Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15
* its contributors may be used to endorse or promote products derived
16
* from this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
#include "MainResourceLoader.h"
33
#include "ApplicationCacheHost.h"
34
#include "BackForwardController.h"
36
#include "DOMWindow.h"
38
#include "DocumentLoadTiming.h"
39
#include "DocumentLoader.h"
40
#include "FormState.h"
42
#include "FrameLoader.h"
43
#include "FrameLoaderClient.h"
44
#include "HTMLFormElement.h"
45
#include "HistoryItem.h"
46
#include "InspectorInstrumentation.h"
47
#include "LoaderStrategy.h"
49
#include "PlatformStrategies.h"
50
#include "ResourceError.h"
51
#include "ResourceHandle.h"
52
#include "ResourceLoadScheduler.h"
53
#include "SchemeRegistry.h"
54
#include "SecurityOrigin.h"
56
#include <wtf/CurrentTime.h>
59
#include "PluginDatabase.h"
62
#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
63
#include "WebCoreSystemInterface.h"
66
// FIXME: More that is in common with SubresourceLoader should move up into ResourceLoader.
70
MainResourceLoader::MainResourceLoader(Frame* frame)
71
: ResourceLoader(frame, ResourceLoaderOptions(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck))
72
, m_dataLoadTimer(this, &MainResourceLoader::handleSubstituteDataLoadNow)
73
, m_loadingMultipartContent(false)
74
, m_waitingForContentPolicy(false)
75
, m_timeOfLastDataReceived(0.0)
76
#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
82
MainResourceLoader::~MainResourceLoader()
84
#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
89
PassRefPtr<MainResourceLoader> MainResourceLoader::create(Frame* frame)
91
return adoptRef(new MainResourceLoader(frame));
94
void MainResourceLoader::receivedError(const ResourceError& error)
96
// Calling receivedMainResourceError will likely result in the last reference to this object to go away.
97
RefPtr<MainResourceLoader> protect(this);
98
RefPtr<Frame> protectFrame(m_frame);
100
// It is important that we call DocumentLoader::mainReceivedError before calling
101
// ResourceLoadNotifier::didFailToLoad because mainReceivedError clears out the relevant
102
// document loaders. Also, mainReceivedError ends up calling a FrameLoadDelegate method
103
// and didFailToLoad calls a ResourceLoadDelegate method and they need to be in the correct order.
104
documentLoader()->mainReceivedError(error);
107
ASSERT(!reachedTerminalState());
108
frameLoader()->notifier()->didFailToLoad(this, error);
113
ASSERT(reachedTerminalState());
116
void MainResourceLoader::willCancel(const ResourceError&)
118
m_dataLoadTimer.stop();
120
if (m_waitingForContentPolicy) {
121
frameLoader()->policyChecker()->cancelCheck();
122
ASSERT(m_waitingForContentPolicy);
123
m_waitingForContentPolicy = false;
124
deref(); // balances ref in didReceiveResponse
128
void MainResourceLoader::didCancel(const ResourceError& error)
130
// We should notify the frame loader after fully canceling the load, because it can do complicated work
131
// like calling DOMWindow::print(), during which a half-canceled load could try to finish.
132
documentLoader()->mainReceivedError(error);
134
#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
136
wkFilterRelease(m_filter);
142
ResourceError MainResourceLoader::interruptedForPolicyChangeError() const
144
return frameLoader()->client()->interruptedForPolicyChangeError(request());
147
void MainResourceLoader::stopLoadingForPolicyChange()
149
ResourceError error = interruptedForPolicyChangeError();
150
error.setIsCancellation(true);
154
void MainResourceLoader::callContinueAfterNavigationPolicy(void* argument, const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue)
156
static_cast<MainResourceLoader*>(argument)->continueAfterNavigationPolicy(request, shouldContinue);
159
void MainResourceLoader::continueAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue)
162
stopLoadingForPolicyChange();
163
else if (m_substituteData.isValid()) {
164
// A redirect resulted in loading substitute data.
165
ASSERT(documentLoader()->timing()->redirectCount());
167
handleSubstituteDataLoadSoon(request);
170
deref(); // balances ref in willSendRequest
173
bool MainResourceLoader::isPostOrRedirectAfterPost(const ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
175
if (newRequest.httpMethod() == "POST")
178
int status = redirectResponse.httpStatusCode();
179
if (((status >= 301 && status <= 303) || status == 307)
180
&& frameLoader()->initialRequest().httpMethod() == "POST")
186
void MainResourceLoader::addData(const char* data, int length, bool allAtOnce)
188
ResourceLoader::addData(data, length, allAtOnce);
189
documentLoader()->receivedData(data, length);
192
void MainResourceLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
194
// Note that there are no asserts here as there are for the other callbacks. This is due to the
195
// fact that this "callback" is sent when starting every load, and the state of callback
196
// deferrals plays less of a part in this function in preventing the bad behavior deferring
197
// callbacks is meant to prevent.
198
ASSERT(!newRequest.isNull());
200
// The additional processing can do anything including possibly removing the last
201
// reference to this object; one example of this is 3266216.
202
RefPtr<MainResourceLoader> protect(this);
204
if (!frameLoader()->checkIfFormActionAllowedByCSP(newRequest.url())) {
209
ASSERT(documentLoader()->timing()->fetchStart());
210
if (!redirectResponse.isNull()) {
211
// If the redirecting url is not allowed to display content from the target origin,
212
// then block the redirect.
213
RefPtr<SecurityOrigin> redirectingOrigin = SecurityOrigin::create(redirectResponse.url());
214
if (!redirectingOrigin->canDisplay(newRequest.url())) {
215
FrameLoader::reportLocalLoadFailed(m_frame.get(), newRequest.url().string());
219
documentLoader()->timing()->addRedirect(redirectResponse.url(), newRequest.url());
222
// Update cookie policy base URL as URL changes, except for subframes, which use the
223
// URL of the main frame which doesn't change when we redirect.
224
if (frameLoader()->isLoadingMainFrame())
225
newRequest.setFirstPartyForCookies(newRequest.url());
227
// If we're fielding a redirect in response to a POST, force a load from origin, since
228
// this is a common site technique to return to a page viewing some data that the POST
230
// Also, POST requests always load from origin, but this does not affect subresources.
231
if (newRequest.cachePolicy() == UseProtocolCachePolicy && isPostOrRedirectAfterPost(newRequest, redirectResponse))
232
newRequest.setCachePolicy(ReloadIgnoringCacheData);
234
Frame* top = m_frame->tree()->top();
235
if (top != m_frame) {
236
if (!frameLoader()->mixedContentChecker()->canDisplayInsecureContent(top->document()->securityOrigin(), newRequest.url())) {
242
ResourceLoader::willSendRequest(newRequest, redirectResponse);
244
// Don't set this on the first request. It is set when the main load was started.
245
m_documentLoader->setRequest(newRequest);
247
if (!redirectResponse.isNull()) {
248
// We checked application cache for initial URL, now we need to check it for redirected one.
249
ASSERT(!m_substituteData.isValid());
250
documentLoader()->applicationCacheHost()->maybeLoadMainResourceForRedirect(newRequest, m_substituteData);
253
// FIXME: Ideally we'd stop the I/O until we hear back from the navigation policy delegate
254
// listener. But there's no way to do that in practice. So instead we cancel later if the
255
// listener tells us to. In practice that means the navigation policy needs to be decided
256
// synchronously for these redirect cases.
257
if (!redirectResponse.isNull()) {
258
ref(); // balanced by deref in continueAfterNavigationPolicy
259
frameLoader()->policyChecker()->checkNavigationPolicy(newRequest, callContinueAfterNavigationPolicy, this);
263
void MainResourceLoader::continueAfterContentPolicy(PolicyAction contentPolicy, const ResourceResponse& r)
265
KURL url = request().url();
266
const String& mimeType = r.mimeType();
268
switch (contentPolicy) {
270
// Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255).
271
bool isRemoteWebArchive = (equalIgnoringCase("application/x-webarchive", mimeType)
273
|| equalIgnoringCase("message/rfc822", mimeType)
275
|| equalIgnoringCase("multipart/related", mimeType))
276
&& !m_substituteData.isValid() && !SchemeRegistry::shouldTreatURLSchemeAsLocal(url.protocol());
277
if (!frameLoader()->client()->canShowMIMEType(mimeType) || isRemoteWebArchive) {
278
frameLoader()->policyChecker()->cannotShowMIMEType(r);
279
// Check reachedTerminalState since the load may have already been canceled inside of _handleUnimplementablePolicyWithErrorCode::.
280
if (!reachedTerminalState())
281
stopLoadingForPolicyChange();
287
case PolicyDownload: {
288
// m_handle can be null, e.g. when loading a substitute resource from application cache.
290
receivedError(cannotShowURLError());
293
InspectorInstrumentation::continueWithPolicyDownload(m_frame.get(), documentLoader(), identifier(), r);
295
// When starting the request, we didn't know that it would result in download and not navigation. Now we know that main document URL didn't change.
296
// Download may use this knowledge for purposes unrelated to cookies, notably for setting file quarantine data.
297
ResourceRequest request = this->request();
298
frameLoader()->setOriginalURLForDownloadRequest(request);
300
frameLoader()->client()->download(m_handle.get(), request, r);
302
// It might have gone missing
304
receivedError(interruptedForPolicyChangeError());
308
InspectorInstrumentation::continueWithPolicyIgnore(m_frame.get(), documentLoader(), identifier(), r);
309
stopLoadingForPolicyChange();
313
ASSERT_NOT_REACHED();
316
RefPtr<MainResourceLoader> protect(this);
319
int status = r.httpStatusCode();
320
if (status < 200 || status >= 300) {
321
bool hostedByObject = frameLoader()->isHostedByObjectElement();
323
frameLoader()->handleFallbackContent();
324
// object elements are no longer rendered after we fallback, so don't
325
// keep trying to process data from their load
332
// we may have cancelled this load as part of switching to fallback content
333
if (!reachedTerminalState())
334
ResourceLoader::didReceiveResponse(r);
336
if (frameLoader() && !frameLoader()->activeDocumentLoader()->isStopping() && m_substituteData.isValid()) {
337
if (m_substituteData.content()->size())
338
didReceiveData(m_substituteData.content()->data(), m_substituteData.content()->size(), m_substituteData.content()->size(), true);
339
if (frameLoader() && !frameLoader()->activeDocumentLoader()->isStopping())
344
void MainResourceLoader::callContinueAfterContentPolicy(void* argument, PolicyAction policy)
346
static_cast<MainResourceLoader*>(argument)->continueAfterContentPolicy(policy);
349
void MainResourceLoader::continueAfterContentPolicy(PolicyAction policy)
351
ASSERT(m_waitingForContentPolicy);
352
m_waitingForContentPolicy = false;
353
if (frameLoader() && !frameLoader()->activeDocumentLoader()->isStopping())
354
continueAfterContentPolicy(policy, m_response);
355
deref(); // balances ref in didReceiveResponse
358
void MainResourceLoader::didReceiveResponse(const ResourceResponse& r)
360
if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainResponse(request(), r))
363
DEFINE_STATIC_LOCAL(AtomicString, xFrameOptionHeader, ("x-frame-options", AtomicString::ConstructFromLiteral));
364
HTTPHeaderMap::const_iterator it = r.httpHeaderFields().find(xFrameOptionHeader);
365
if (it != r.httpHeaderFields().end()) {
366
String content = it->value;
367
if (m_frame->loader()->shouldInterruptLoadForXFrameOptions(content, r.url(), identifier())) {
368
InspectorInstrumentation::continueAfterXFrameOptionsDenied(m_frame.get(), documentLoader(), identifier(), r);
369
String message = "Refused to display '" + r.url().string() + "' in a frame because it set 'X-Frame-Options' to '" + content + "'.";
370
m_frame->document()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, r.url().string(), 0, 0, identifier());
377
// There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
378
// See <rdar://problem/6304600> for more details.
380
ASSERT(!defersLoading());
383
if (m_loadingMultipartContent) {
384
m_documentLoader->setupForReplace();
389
m_loadingMultipartContent = true;
391
// The additional processing can do anything including possibly removing the last
392
// reference to this object; one example of this is 3266216.
393
RefPtr<MainResourceLoader> protect(this);
395
m_documentLoader->setResponse(r);
399
ASSERT(!m_waitingForContentPolicy);
400
m_waitingForContentPolicy = true;
401
ref(); // balanced by deref in continueAfterContentPolicy and didCancel
403
ASSERT(frameLoader()->activeDocumentLoader());
405
// Always show content with valid substitute data.
406
if (frameLoader()->activeDocumentLoader()->substituteData().isValid()) {
407
callContinueAfterContentPolicy(this, PolicyUse);
412
// Respect the hidden FTP Directory Listing pref so it can be tested even if the policy delegate might otherwise disallow it
413
Settings* settings = m_frame->settings();
414
if (settings && settings->forceFTPDirectoryListings() && m_response.mimeType() == "application/x-ftp-directory") {
415
callContinueAfterContentPolicy(this, PolicyUse);
420
#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
421
if (r.url().protocolIs("https") && wkFilterIsManagedSession())
422
m_filter = wkFilterCreateInstance(r.nsURLResponse());
425
frameLoader()->policyChecker()->checkContentPolicy(m_response, callContinueAfterContentPolicy, this);
428
void MainResourceLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)
433
ASSERT(!m_response.isNull());
435
#if USE(CFNETWORK) || PLATFORM(MAC)
436
// Workaround for <rdar://problem/6060782>
437
if (m_response.isNull()) {
438
m_response = ResourceResponse(KURL(), "text/html", 0, String(), String());
439
if (DocumentLoader* documentLoader = frameLoader()->activeDocumentLoader())
440
documentLoader->setResponse(m_response);
444
// There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
445
// See <rdar://problem/6304600> for more details.
447
ASSERT(!defersLoading());
450
#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
452
ASSERT(!wkFilterWasBlocked(m_filter));
453
const char* blockedData = wkFilterAddData(m_filter, data, &length);
454
// If we don't have blockedData, that means we're still accumulating data
456
// Transition to committed state.
457
ResourceLoader::didReceiveData("", 0, 0, false);
462
encodedDataLength = -1;
466
documentLoader()->applicationCacheHost()->mainResourceDataReceived(data, length, encodedDataLength, allAtOnce);
468
// The additional processing can do anything including possibly removing the last
469
// reference to this object; one example of this is 3266216.
470
RefPtr<MainResourceLoader> protect(this);
472
m_timeOfLastDataReceived = monotonicallyIncreasingTime();
474
ResourceLoader::didReceiveData(data, length, encodedDataLength, allAtOnce);
476
#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
477
if (WebFilterEvaluator *filter = m_filter) {
478
// If we got here, it means we know if we were blocked or not. If we were blocked, we're
479
// done loading the page altogether. Either way, we don't need the filter anymore.
481
// Remove this->m_filter early so didFinishLoading doesn't see it.
483
if (wkFilterWasBlocked(filter))
485
wkFilterRelease(filter);
490
void MainResourceLoader::didFinishLoading(double finishTime)
492
// There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
493
// See <rdar://problem/6304600> for more details.
495
ASSERT(!defersLoading() || InspectorInstrumentation::isDebuggerPaused(m_frame.get()));
498
// The additional processing can do anything including possibly removing the last
499
// reference to this object.
500
RefPtr<MainResourceLoader> protect(this);
501
RefPtr<DocumentLoader> dl = documentLoader();
503
#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
506
const char* data = wkFilterDataComplete(m_filter, &length);
507
WebFilterEvaluator *filter = m_filter;
508
// Remove this->m_filter early so didReceiveData doesn't see it.
511
didReceiveData(data, length, -1, false);
512
wkFilterRelease(filter);
516
if (m_loadingMultipartContent)
517
dl->maybeFinishLoadingMultipartContent();
519
documentLoader()->timing()->setResponseEnd(finishTime ? finishTime : (m_timeOfLastDataReceived ? m_timeOfLastDataReceived : monotonicallyIncreasingTime()));
520
documentLoader()->finishedLoading();
521
ResourceLoader::didFinishLoading(finishTime);
523
dl->applicationCacheHost()->finishedLoadingMainResource();
526
void MainResourceLoader::didFail(const ResourceError& error)
528
#if PLATFORM(MAC) && !PLATFORM(IOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
530
wkFilterRelease(m_filter);
535
if (documentLoader()->applicationCacheHost()->maybeLoadFallbackForMainError(request(), error))
538
// There is a bug in CFNetwork where callbacks can be dispatched even when loads are deferred.
539
// See <rdar://problem/6304600> for more details.
541
ASSERT(!defersLoading());
544
receivedError(error);
547
void MainResourceLoader::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
549
MemoryClassInfo info(memoryObjectInfo, this, WebCoreMemoryTypes::Loader);
550
ResourceLoader::reportMemoryUsage(memoryObjectInfo);
551
info.addMember(m_initialRequest);
552
info.addMember(m_substituteData);
553
info.addMember(m_dataLoadTimer);
556
void MainResourceLoader::handleSubstituteDataLoadNow(MainResourceLoaderTimer*)
558
RefPtr<MainResourceLoader> protect(this);
560
KURL url = m_substituteData.responseURL();
562
url = m_initialRequest.url();
564
// Clear the initial request here so that subsequent entries into the
565
// loader will not think there's still a deferred load left to do.
566
m_initialRequest = ResourceRequest();
568
ResourceResponse response(url, m_substituteData.mimeType(), m_substituteData.content()->size(), m_substituteData.textEncoding(), "");
569
didReceiveResponse(response);
572
void MainResourceLoader::startDataLoadTimer()
574
m_dataLoadTimer.startOneShot(0);
576
#if HAVE(RUNLOOP_TIMER)
577
if (SchedulePairHashSet* scheduledPairs = m_frame->page()->scheduledRunLoopPairs())
578
m_dataLoadTimer.schedule(*scheduledPairs);
582
void MainResourceLoader::handleSubstituteDataLoadSoon(const ResourceRequest& r)
584
m_initialRequest = r;
586
if (m_documentLoader->deferMainResourceDataLoad())
587
startDataLoadTimer();
589
handleSubstituteDataLoadNow(0);
592
void MainResourceLoader::loadNow(ResourceRequest& r)
595
ASSERT(!defersLoading());
597
#if USE(PLATFORM_STRATEGIES)
598
platformStrategies()->loaderStrategy()->resourceLoadScheduler()->addMainResourceLoad(this);
600
resourceLoadScheduler()->addMainResourceLoad(this);
603
if (m_substituteData.isValid())
604
handleSubstituteDataLoadSoon(r);
606
m_handle = ResourceHandle::create(m_frame->loader()->networkingContext(), r, this, false, true);
611
void MainResourceLoader::load(const ResourceRequest& r, const SubstituteData& substituteData)
615
// It appears that it is possible for this load to be cancelled and derefenced by the DocumentLoader
616
// in willSendRequest() if loadNow() is called.
617
RefPtr<MainResourceLoader> protect(this);
619
m_substituteData = substituteData;
621
ASSERT(documentLoader()->timing()->navigationStart());
622
ASSERT(!documentLoader()->timing()->fetchStart());
623
documentLoader()->timing()->markFetchStart();
624
ResourceRequest request(r);
626
// Send this synthetic delegate callback since clients expect it, and
627
// we no longer send the callback from within NSURLConnection for
629
willSendRequest(request, ResourceResponse());
630
ASSERT(!deletionHasBegun());
632
// <rdar://problem/4801066>
633
// willSendRequest() is liable to make the call to frameLoader() return null, so we need to check that here
634
if (!frameLoader() || request.isNull()) {
635
if (!reachedTerminalState())
640
documentLoader()->applicationCacheHost()->maybeLoadMainResource(request, m_substituteData);
643
m_initialRequest = request;
648
void MainResourceLoader::setDefersLoading(bool defers)
650
ResourceLoader::setDefersLoading(defers);
653
if (m_dataLoadTimer.isActive())
654
m_dataLoadTimer.stop();
656
if (m_initialRequest.isNull())
659
ResourceRequest initialRequest(m_initialRequest);
660
m_initialRequest = ResourceRequest();
661
loadNow(initialRequest);