2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #import <WebKit/WebMainResourceLoader.h>
31 #import <Foundation/NSHTTPCookie.h>
32 #import <Foundation/NSURLConnection.h>
33 #import <Foundation/NSURLRequest.h>
34 #import <Foundation/NSURLResponse.h>
36 #import <WebKit/DOMHTML.h>
37 #import <WebKit/WebDataProtocol.h>
38 #import <WebKit/WebDataSourceInternal.h>
39 #import <WebKit/WebDocument.h>
40 #import <WebKit/WebFrameView.h>
41 #import <WebKit/WebFrameLoader.h>
42 #import <WebKit/WebFrameInternal.h>
43 #import <WebKit/WebKitErrors.h>
44 #import <WebKit/WebKitErrorsPrivate.h>
45 #import <WebKit/WebKitLogging.h>
46 #import <WebKit/WebKitNSStringExtras.h>
47 #import <WebKit/WebNSObjectExtras.h>
48 #import <WebKit/WebNSURLExtras.h>
49 #import <WebKit/WebViewInternal.h>
51 // FIXME: More that is in common with WebSubresourceLoader should move up into WebLoader.
53 @implementation WebMainResourceLoader
55 - (id)initWithFrameLoader:(WebFrameLoader *)fl
60 [self setFrameLoader:fl];
61 proxy = WKCreateNSURLConnectionDelegateProxy();
62 [proxy setDelegate:self];
70 [_initialRequest release];
72 [proxy setDelegate:nil];
80 [proxy setDelegate:nil];
84 - (void)receivedError:(NSError *)error
86 // Calling _receivedMainResourceError will likely result in a call to release, so we must retain.
88 WebFrameLoader *fl = [frameLoader retain]; // super's didFailWithError will release the frameLoader
91 ASSERT(!reachedTerminalState);
92 [frameLoader _didFailLoadingWithError:error forResource:identifier];
95 [fl _receivedMainResourceError:error complete:YES];
98 [self releaseResources];
100 ASSERT(reachedTerminalState);
106 - (void)cancelContentPolicy
108 [listener _invalidate];
111 [policyResponse release];
112 policyResponse = nil;
115 -(void)cancelWithError:(NSError *)error
117 // Calling _receivedMainResourceError will likely result in a call to release, so we must retain.
120 [self cancelContentPolicy];
121 [frameLoader retain];
122 [frameLoader _receivedMainResourceError:error complete:YES];
123 [frameLoader release];
124 [super cancelWithError:error];
129 - (NSError *)interruptForPolicyChangeError
131 return [NSError _webKitErrorWithDomain:WebKitErrorDomain code:WebKitErrorFrameLoadInterruptedByPolicyChange URL:[request URL]];
134 -(void)stopLoadingForPolicyChange
137 [self cancelWithError:[self interruptForPolicyChangeError]];
141 -(void)continueAfterNavigationPolicy:(NSURLRequest *)_request formState:(WebFormState *)state
144 [self stopLoadingForPolicyChange];
148 - (BOOL)_isPostOrRedirectAfterPost:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
152 if ([[newRequest HTTPMethod] isEqualToString:@"POST"]) {
155 else if (redirectResponse && [redirectResponse isKindOfClass:[NSHTTPURLResponse class]]) {
156 int status = [(NSHTTPURLResponse *)redirectResponse statusCode];
157 if (((status >= 301 && status <= 303) || status == 307)
158 && [[[frameLoader initialRequest] HTTPMethod] isEqualToString:@"POST"]) {
166 - (void)addData:(NSData *)data
168 [super addData:data];
169 [frameLoader _receivedData:data];
174 // Override. We don't want to save the main resource as a subresource of the data source.
177 - (NSURLRequest *)willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
179 // Note that there are no asserts here as there are for the other callbacks. This is due to the
180 // fact that this "callback" is sent when starting every load, and the state of callback
181 // deferrals plays less of a part in this function in preventing the bad behavior deferring
182 // callbacks is meant to prevent.
183 ASSERT(newRequest != nil);
185 // retain/release self in this delegate method since the additional processing can do
186 // anything including possibly releasing self; one example of this is 3266216
189 NSURL *URL = [newRequest URL];
191 LOG(Redirect, "URL = %@", URL);
193 NSMutableURLRequest *mutableRequest = nil;
194 // Update cookie policy base URL as URL changes, except for subframes, which use the
195 // URL of the main frame which doesn't change when we redirect.
196 if ([[frameLoader webFrame] _isMainFrame]) {
197 mutableRequest = [newRequest mutableCopy];
198 [mutableRequest setMainDocumentURL:URL];
201 // If we're fielding a redirect in response to a POST, force a load from origin, since
202 // this is a common site technique to return to a page viewing some data that the POST
204 // Also, POST requests always load from origin, but this does not affect subresources.
205 if ([newRequest cachePolicy] == NSURLRequestUseProtocolCachePolicy &&
206 [self _isPostOrRedirectAfterPost:newRequest redirectResponse:redirectResponse]) {
207 if (!mutableRequest) {
208 mutableRequest = [newRequest mutableCopy];
210 [mutableRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
212 if (mutableRequest) {
213 newRequest = [mutableRequest autorelease];
216 // Note super will make a copy for us, so reassigning newRequest is important. Since we are returning this value, but
217 // it's only guaranteed to be retained by self, and self might be dealloc'ed in this method, we have to autorelease.
218 // See 3777253 for an example.
219 newRequest = [[[super willSendRequest:newRequest redirectResponse:redirectResponse] retain] autorelease];
221 // Don't set this on the first request. It is set
222 // when the main load was started.
223 [frameLoader _setRequest:newRequest];
225 [[frameLoader webFrame] _checkNavigationPolicyForRequest:newRequest
226 dataSource:[frameLoader activeDataSource]
229 withSelector:@selector(continueAfterNavigationPolicy:formState:)];
235 -(void)continueAfterContentPolicy:(WebPolicyAction)contentPolicy response:(NSURLResponse *)r
237 NSURL *URL = [request URL];
238 NSString *MIMEType = [r MIMEType];
240 switch (contentPolicy) {
243 // Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255).
244 BOOL isRemote = ![URL isFileURL] && ![WebDataProtocol _webIsDataProtocolURL:URL];
245 BOOL isRemoteWebArchive = isRemote && [MIMEType _webkit_isCaseInsensitiveEqualToString:@"application/x-webarchive"];
246 if (![WebDataSource _canShowMIMEType:MIMEType] || isRemoteWebArchive) {
247 [[frameLoader webFrame] _handleUnimplementablePolicyWithErrorCode:WebKitErrorCannotShowMIMEType forURL:URL];
248 // Check reachedTerminalState since the load may have already been cancelled inside of _handleUnimplementablePolicyWithErrorCode::.
249 if (!reachedTerminalState) {
250 [self stopLoadingForPolicyChange];
256 case WebPolicyDownload:
257 [proxy setDelegate:nil];
258 [frameLoader _downloadWithLoadingConnection:connection request:request response:r proxy:proxy];
262 [self receivedError:[self interruptForPolicyChangeError]];
265 case WebPolicyIgnore:
266 [self stopLoadingForPolicyChange];
270 ASSERT_NOT_REACHED();
275 if ([r isKindOfClass:[NSHTTPURLResponse class]]) {
276 int status = [(NSHTTPURLResponse *)r statusCode];
277 if (status < 200 || status >= 300) {
278 // Handle <object> fallback for error cases.
279 DOMHTMLElement *hostElement = [[frameLoader webFrame] frameElement];
280 [frameLoader _handleFallbackContent];
281 if (hostElement && [hostElement isKindOfClass:[DOMHTMLObjectElement class]])
282 // object elements are no longer rendered after we fallback, so don't
283 // keep trying to process data from their load
288 // we may have cancelled this load as part of switching to fallback content
289 if (!reachedTerminalState) {
290 [super didReceiveResponse:r];
293 if (![frameLoader _isStopping] && ([URL _webkit_shouldLoadAsEmptyDocument] || [WebView _representationExistsForURLScheme:[URL scheme]])) {
294 [self didFinishLoading];
300 -(void)continueAfterContentPolicy:(WebPolicyAction)policy
302 NSURLResponse *r = [policyResponse retain];
303 BOOL isStopping = [frameLoader _isStopping];
305 [self cancelContentPolicy];
307 [self continueAfterContentPolicy:policy response:r];
312 -(void)checkContentPolicyForResponse:(NSURLResponse *)r
314 WebPolicyDecisionListener *l = [[WebPolicyDecisionListener alloc]
315 _initWithTarget:self action:@selector(continueAfterContentPolicy:)];
317 policyResponse = [r retain];
320 [frameLoader _decidePolicyForMIMEType:[r MIMEType] decisionListener:listener];
325 - (void)didReceiveResponse:(NSURLResponse *)r
327 ASSERT([[r URL] _webkit_shouldLoadAsEmptyDocument] || ![self defersCallbacks]);
328 ASSERT([[r URL] _webkit_shouldLoadAsEmptyDocument] || ![frameLoader _defersCallbacks]);
330 LOG(Loading, "main content type: %@", [r MIMEType]);
332 if (loadingMultipartContent) {
333 [frameLoader _setupForReplaceByMIMEType:[r MIMEType]];
334 [self clearResourceData];
337 if ([[r MIMEType] isEqualToString:@"multipart/x-mixed-replace"])
338 loadingMultipartContent = YES;
340 // FIXME: This is a workaround to make web archive files work with Foundations that
341 // are too old to know about web archive files. We should remove this before we ship.
342 NSURL *URL = [r URL];
343 if ([[[URL path] pathExtension] _webkit_isCaseInsensitiveEqualToString:@"webarchive"]) {
344 r = [[[NSURLResponse alloc] initWithURL:URL
345 MIMEType:@"application/x-webarchive"
346 expectedContentLength:[r expectedContentLength]
347 textEncodingName:[r textEncodingName]] autorelease];
350 // retain/release self in this delegate method since the additional processing can do
351 // anything including possibly releasing self; one example of this is 3266216
353 [frameLoader _setResponse:r];
354 _contentLength = [r expectedContentLength];
356 [self checkContentPolicyForResponse:r];
360 - (void)didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
363 ASSERT([data length] != 0);
364 ASSERT(![self defersCallbacks]);
365 ASSERT(![frameLoader _defersCallbacks]);
367 LOG(Loading, "URL = %@, data = %p, length %d", [frameLoader _URL], data, [data length]);
369 // retain/release self in this delegate method since the additional processing can do
370 // anything including possibly releasing self; one example of this is 3266216
372 [frameLoader _mainReceivedBytesSoFar:_bytesReceived complete:NO];
374 [super didReceiveData:data lengthReceived:lengthReceived];
375 _bytesReceived += [data length];
377 LOG(Loading, "%d of %d", _bytesReceived, _contentLength);
381 - (void)didFinishLoading
383 ASSERT([[frameLoader _URL] _webkit_shouldLoadAsEmptyDocument] || ![self defersCallbacks]);
384 ASSERT([[frameLoader _URL] _webkit_shouldLoadAsEmptyDocument] || ![frameLoader _defersCallbacks]);
386 LOG(Loading, "URL = %@", [frameLoader _URL]);
388 // Calls in this method will most likely result in a call to release, so we must retain.
391 [frameLoader _finishedLoading];
392 [frameLoader _mainReceivedBytesSoFar:_bytesReceived complete:YES];
393 [super didFinishLoading];
398 - (void)didFailWithError:(NSError *)error
400 ASSERT(![self defersCallbacks]);
401 ASSERT(![frameLoader _defersCallbacks]);
403 [self receivedError:error];
406 - (NSURLRequest *)loadWithRequestNow:(NSURLRequest *)r
408 BOOL shouldLoadEmptyBeforeRedirect = [[r URL] _webkit_shouldLoadAsEmptyDocument];
410 ASSERT(connection == nil);
411 ASSERT(shouldLoadEmptyBeforeRedirect || ![self defersCallbacks]);
412 ASSERT(shouldLoadEmptyBeforeRedirect || ![frameLoader _defersCallbacks]);
414 // Send this synthetic delegate callback since clients expect it, and
415 // we no longer send the callback from within NSURLConnection for
417 r = [self willSendRequest:r redirectResponse:nil];
418 NSURL *URL = [r URL];
419 BOOL shouldLoadEmpty = [URL _webkit_shouldLoadAsEmptyDocument];
421 if (shouldLoadEmptyBeforeRedirect && !shouldLoadEmpty && [self defersCallbacks]) {
425 if (shouldLoadEmpty || [WebDataSource _representationExistsForURLScheme:[URL scheme]]) {
427 if (shouldLoadEmpty) {
428 MIMEType = @"text/html";
430 MIMEType = [WebDataSource _generatedMIMETypeForURLScheme:[URL scheme]];
433 NSURLResponse *resp = [[NSURLResponse alloc] initWithURL:URL MIMEType:MIMEType
434 expectedContentLength:0 textEncodingName:nil];
435 [self didReceiveResponse:resp];
438 connection = [[NSURLConnection alloc] initWithRequest:r delegate:proxy];
444 - (BOOL)loadWithRequest:(NSURLRequest *)r
446 ASSERT(connection == nil);
448 BOOL defer = [self defersCallbacks];
450 NSURL *URL = [r URL];
451 BOOL shouldLoadEmpty = [URL _webkit_shouldLoadAsEmptyDocument];
452 if (shouldLoadEmpty) {
457 r = [self loadWithRequestNow:r];
459 // Started as an empty document, but was redirected to something non-empty.
460 ASSERT([self defersCallbacks]);
465 NSURLRequest *copy = [r copy];
466 [_initialRequest release];
467 _initialRequest = copy;
473 - (void)setDefersCallbacks:(BOOL)defers
475 [super setDefersCallbacks:defers];
477 NSURLRequest *r = _initialRequest;
479 _initialRequest = nil;
480 [self loadWithRequestNow:r];