WebKit:
[WebKit-https.git] / WebKit / Loader / WebMainResourceLoader.m
1 /*
2  * Copyright (C) 2005 Apple Computer, 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 #import <WebKit/WebMainResourceLoader.h>
30
31 #import <Foundation/NSHTTPCookie.h>
32 #import <Foundation/NSURLConnection.h>
33 #import <Foundation/NSURLRequest.h>
34 #import <Foundation/NSURLResponse.h>
35
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>
50
51 // FIXME: More that is in common with WebSubresourceLoader should move up into WebLoader.
52
53 @implementation WebMainResourceLoader
54
55 - (id)initWithFrameLoader:(WebFrameLoader *)fl
56 {
57     self = [super init];
58     
59     if (self) {
60         [self setFrameLoader:fl];
61         proxy = WKCreateNSURLConnectionDelegateProxy();
62         [proxy setDelegate:self];
63     }
64
65     return self;
66 }
67
68 - (void)dealloc
69 {
70     [_initialRequest release];
71
72     [proxy setDelegate:nil];
73     [proxy release];
74     
75     [super dealloc];
76 }
77
78 - (void)finalize
79 {
80     [proxy setDelegate:nil];
81     [super finalize];
82 }
83
84 - (void)receivedError:(NSError *)error
85 {
86     // Calling _receivedMainResourceError will likely result in a call to release, so we must retain.
87     [self retain];
88     WebFrameLoader *fl = [frameLoader retain]; // super's didFailWithError will release the frameLoader
89
90     if (!cancelledFlag) {
91         ASSERT(!reachedTerminalState);
92         [frameLoader _didFailLoadingWithError:error forResource:identifier];
93     }
94
95     [fl _receivedMainResourceError:error complete:YES];
96
97     if (!cancelledFlag)
98         [self releaseResources];
99
100     ASSERT(reachedTerminalState);
101
102     [fl release];
103     [self release];
104 }
105
106 - (void)cancelContentPolicy
107 {
108     [listener _invalidate];
109     [listener release];
110     listener = nil;
111     [policyResponse release];
112     policyResponse = nil;
113 }
114
115 -(void)cancelWithError:(NSError *)error
116 {
117     // Calling _receivedMainResourceError will likely result in a call to release, so we must retain.
118     [self retain];
119
120     [self cancelContentPolicy];
121     [frameLoader retain];
122     [frameLoader _receivedMainResourceError:error complete:YES];
123     [frameLoader release];
124     [super cancelWithError:error];
125
126     [self release];
127 }
128
129 - (NSError *)interruptForPolicyChangeError
130 {
131     return [NSError _webKitErrorWithDomain:WebKitErrorDomain code:WebKitErrorFrameLoadInterruptedByPolicyChange URL:[request URL]];
132 }
133
134 -(void)stopLoadingForPolicyChange
135 {
136     [self retain];
137     [self cancelWithError:[self interruptForPolicyChangeError]];
138     [self release];
139 }
140
141 -(void)continueAfterNavigationPolicy:(NSURLRequest *)_request formState:(WebFormState *)state
142 {
143     if (!_request) {
144         [self stopLoadingForPolicyChange];
145     }
146 }
147
148 - (BOOL)_isPostOrRedirectAfterPost:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
149 {
150     BOOL result = NO;
151     
152     if ([[newRequest HTTPMethod] isEqualToString:@"POST"]) {
153         result = YES;
154     }
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"]) {
159             result = YES;
160         }
161     }
162     
163     return result;
164 }
165
166 - (void)addData:(NSData *)data
167 {
168     [super addData:data];
169     [frameLoader _receivedData:data];
170 }
171
172 - (void)saveResource
173 {
174     // Override. We don't want to save the main resource as a subresource of the data source.
175 }
176
177 - (NSURLRequest *)willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
178 {
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);
184
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
187     [self retain];
188
189     NSURL *URL = [newRequest URL];
190
191     LOG(Redirect, "URL = %@", URL);
192
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];
199     }
200
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
203     // just modified.
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];
209         }
210         [mutableRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
211     }
212     if (mutableRequest) {
213         newRequest = [mutableRequest autorelease];
214     }
215
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];
220
221     // Don't set this on the first request.  It is set
222     // when the main load was started.
223     [frameLoader _setRequest:newRequest];
224     
225     [[frameLoader webFrame] _checkNavigationPolicyForRequest:newRequest
226                                                   dataSource:[frameLoader activeDataSource]
227                                                    formState:nil
228                                                      andCall:self
229                                                 withSelector:@selector(continueAfterNavigationPolicy:formState:)];
230
231     [self release];
232     return newRequest;
233 }
234
235 -(void)continueAfterContentPolicy:(WebPolicyAction)contentPolicy response:(NSURLResponse *)r
236 {
237     NSURL *URL = [request URL];
238     NSString *MIMEType = [r MIMEType]; 
239     
240     switch (contentPolicy) {
241     case WebPolicyUse:
242     {
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];
251             }
252             return;
253         }
254         break;
255     }
256     case WebPolicyDownload:
257         [proxy setDelegate:nil];
258         [frameLoader _downloadWithLoadingConnection:connection request:request response:r proxy:proxy];
259         [proxy release];
260         proxy = nil;
261
262         [self receivedError:[self interruptForPolicyChangeError]];
263         return;
264
265     case WebPolicyIgnore:
266         [self stopLoadingForPolicyChange];
267         return;
268     
269     default:
270         ASSERT_NOT_REACHED();
271     }
272
273     [self retain];
274
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
284                 [self cancel];
285         }
286     }
287
288     // we may have cancelled this load as part of switching to fallback content
289     if (!reachedTerminalState) {
290         [super didReceiveResponse:r];
291     }
292
293     if (![frameLoader _isStopping] && ([URL _webkit_shouldLoadAsEmptyDocument] || [WebView _representationExistsForURLScheme:[URL scheme]])) {
294         [self didFinishLoading];
295     }
296     
297     [self release];
298 }
299
300 -(void)continueAfterContentPolicy:(WebPolicyAction)policy
301 {
302     NSURLResponse *r = [policyResponse retain];
303     BOOL isStopping = [frameLoader _isStopping];
304
305     [self cancelContentPolicy];
306     if (!isStopping)
307         [self continueAfterContentPolicy:policy response:r];
308
309     [r release];
310 }
311
312 -(void)checkContentPolicyForResponse:(NSURLResponse *)r
313 {
314     WebPolicyDecisionListener *l = [[WebPolicyDecisionListener alloc]
315                                        _initWithTarget:self action:@selector(continueAfterContentPolicy:)];
316     listener = l;
317     policyResponse = [r retain];
318
319     [l retain];
320     [frameLoader _decidePolicyForMIMEType:[r MIMEType] decisionListener:listener];
321     [l release];
322 }
323
324
325 - (void)didReceiveResponse:(NSURLResponse *)r
326 {
327     ASSERT([[r URL] _webkit_shouldLoadAsEmptyDocument] || ![self defersCallbacks]);
328     ASSERT([[r URL] _webkit_shouldLoadAsEmptyDocument] || ![frameLoader _defersCallbacks]);
329
330     LOG(Loading, "main content type: %@", [r MIMEType]);
331     
332     if (loadingMultipartContent) {
333         [frameLoader _setupForReplaceByMIMEType:[r MIMEType]];
334         [self clearResourceData];
335     }
336     
337     if ([[r MIMEType] isEqualToString:@"multipart/x-mixed-replace"])
338         loadingMultipartContent = YES;
339         
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];
348     }
349     
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
352     [self retain];
353     [frameLoader _setResponse:r];
354     _contentLength = [r expectedContentLength];
355
356     [self checkContentPolicyForResponse:r];
357     [self release];
358 }
359
360 - (void)didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived
361 {
362     ASSERT(data);
363     ASSERT([data length] != 0);
364     ASSERT(![self defersCallbacks]);
365     ASSERT(![frameLoader _defersCallbacks]);
366  
367     LOG(Loading, "URL = %@, data = %p, length %d", [frameLoader _URL], data, [data length]);
368
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
371     [self retain];
372     [frameLoader _mainReceivedBytesSoFar:_bytesReceived complete:NO];
373     
374     [super didReceiveData:data lengthReceived:lengthReceived];
375     _bytesReceived += [data length];
376
377     LOG(Loading, "%d of %d", _bytesReceived, _contentLength);
378     [self release];
379 }
380
381 - (void)didFinishLoading
382 {
383     ASSERT([[frameLoader _URL] _webkit_shouldLoadAsEmptyDocument] || ![self defersCallbacks]);
384     ASSERT([[frameLoader _URL] _webkit_shouldLoadAsEmptyDocument] || ![frameLoader _defersCallbacks]);
385
386     LOG(Loading, "URL = %@", [frameLoader _URL]);
387         
388     // Calls in this method will most likely result in a call to release, so we must retain.
389     [self retain];
390
391     [frameLoader _finishedLoading];
392     [frameLoader _mainReceivedBytesSoFar:_bytesReceived complete:YES];
393     [super didFinishLoading];
394
395     [self release];
396 }
397
398 - (void)didFailWithError:(NSError *)error
399 {
400     ASSERT(![self defersCallbacks]);
401     ASSERT(![frameLoader _defersCallbacks]);
402
403     [self receivedError:error];
404 }
405
406 - (NSURLRequest *)loadWithRequestNow:(NSURLRequest *)r
407 {
408     BOOL shouldLoadEmptyBeforeRedirect = [[r URL] _webkit_shouldLoadAsEmptyDocument];
409
410     ASSERT(connection == nil);
411     ASSERT(shouldLoadEmptyBeforeRedirect || ![self defersCallbacks]);
412     ASSERT(shouldLoadEmptyBeforeRedirect || ![frameLoader _defersCallbacks]);
413
414     // Send this synthetic delegate callback since clients expect it, and
415     // we no longer send the callback from within NSURLConnection for
416     // initial requests.
417     r = [self willSendRequest:r redirectResponse:nil];
418     NSURL *URL = [r URL];
419     BOOL shouldLoadEmpty = [URL _webkit_shouldLoadAsEmptyDocument];
420
421     if (shouldLoadEmptyBeforeRedirect && !shouldLoadEmpty && [self defersCallbacks]) {
422         return r;
423     }
424
425     if (shouldLoadEmpty || [WebDataSource _representationExistsForURLScheme:[URL scheme]]) {
426         NSString *MIMEType;
427         if (shouldLoadEmpty) {
428             MIMEType = @"text/html";
429         } else {
430             MIMEType = [WebDataSource _generatedMIMETypeForURLScheme:[URL scheme]];
431         }
432
433         NSURLResponse *resp = [[NSURLResponse alloc] initWithURL:URL MIMEType:MIMEType
434             expectedContentLength:0 textEncodingName:nil];
435         [self didReceiveResponse:resp];
436         [resp release];
437     } else {
438         connection = [[NSURLConnection alloc] initWithRequest:r delegate:proxy];
439     }
440
441     return nil;
442 }
443
444 - (BOOL)loadWithRequest:(NSURLRequest *)r
445 {
446     ASSERT(connection == nil);
447
448     BOOL defer = [self defersCallbacks];
449     if (defer) {
450         NSURL *URL = [r URL];
451         BOOL shouldLoadEmpty = [URL _webkit_shouldLoadAsEmptyDocument];
452         if (shouldLoadEmpty) {
453             defer = NO;
454         }
455     }
456     if (!defer) {
457         r = [self loadWithRequestNow:r];
458         if (r != nil) {
459             // Started as an empty document, but was redirected to something non-empty.
460             ASSERT([self defersCallbacks]);
461             defer = YES;
462         }
463     }
464     if (defer) {
465         NSURLRequest *copy = [r copy];
466         [_initialRequest release];
467         _initialRequest = copy;
468     }
469
470     return YES;
471 }
472
473 - (void)setDefersCallbacks:(BOOL)defers
474 {
475     [super setDefersCallbacks:defers];
476     if (!defers) {
477         NSURLRequest *r = _initialRequest;
478         if (r != nil) {
479             _initialRequest = nil;
480             [self loadWithRequestNow:r];
481             [r release];
482         }
483     }
484 }
485
486 @end