Reviewed by Adele (preliminary version) and later by Kevin.
[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 #import <JavaScriptCore/Assertions.h>
36
37 #import <WebKit/WebDataProtocol.h>
38 #import <WebKit/WebNSURLExtras.h>
39 #import <WebKit/WebFrameLoader.h>
40
41 // FIXME: More that is in common with WebSubresourceLoader should move up into WebLoader.
42
43 @implementation WebMainResourceLoader
44
45 - (id)initWithFrameLoader:(WebFrameLoader *)fl
46 {
47     self = [super init];
48     
49     if (self) {
50         [self setFrameLoader:fl];
51         proxy = WKCreateNSURLConnectionDelegateProxy();
52         [proxy setDelegate:self];
53     }
54
55     return self;
56 }
57
58 - (void)dealloc
59 {
60     [_initialRequest release];
61
62     [proxy setDelegate:nil];
63     [proxy release];
64     
65     [super dealloc];
66 }
67
68 - (void)finalize
69 {
70     [proxy setDelegate:nil];
71     [super finalize];
72 }
73
74 - (void)receivedError:(NSError *)error
75 {
76     // Calling _receivedMainResourceError will likely result in a call to release, so we must retain.
77     [self retain];
78     WebFrameLoader *fl = [frameLoader retain]; // super's didFailWithError will release the frameLoader
79
80     if (!cancelledFlag) {
81         ASSERT(!reachedTerminalState);
82         [frameLoader _didFailLoadingWithError:error forResource:identifier];
83     }
84
85     [fl _receivedMainResourceError:error complete:YES];
86
87     if (!cancelledFlag)
88         [self releaseResources];
89
90     ASSERT(reachedTerminalState);
91
92     [fl release];
93     [self release];
94 }
95
96 - (void)cancelContentPolicy
97 {
98     [listener _invalidate];
99     [listener release];
100     listener = nil;
101     [policyResponse release];
102     policyResponse = nil;
103 }
104
105 -(void)cancelWithError:(NSError *)error
106 {
107     // Calling _receivedMainResourceError will likely result in a call to release, so we must retain.
108     [self retain];
109
110     [self cancelContentPolicy];
111     [frameLoader retain];
112     [frameLoader _receivedMainResourceError:error complete:YES];
113     [frameLoader release];
114     [super cancelWithError:error];
115
116     [self release];
117 }
118
119 - (NSError *)interruptForPolicyChangeError
120 {
121     return [frameLoader interruptForPolicyChangeErrorWithRequest:request];
122 }
123
124 -(void)stopLoadingForPolicyChange
125 {
126     [self retain];
127     [self cancelWithError:[self interruptForPolicyChangeError]];
128     [self release];
129 }
130
131 -(void)continueAfterNavigationPolicy:(NSURLRequest *)_request formState:(WebFormState *)state
132 {
133     if (!_request) {
134         [self stopLoadingForPolicyChange];
135     }
136 }
137
138 - (BOOL)_isPostOrRedirectAfterPost:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
139 {
140     BOOL result = NO;
141     
142     if ([[newRequest HTTPMethod] isEqualToString:@"POST"]) {
143         result = YES;
144     }
145     else if (redirectResponse && [redirectResponse isKindOfClass:[NSHTTPURLResponse class]]) {
146         int status = [(NSHTTPURLResponse *)redirectResponse statusCode];
147         if (((status >= 301 && status <= 303) || status == 307)
148             && [[[frameLoader initialRequest] HTTPMethod] isEqualToString:@"POST"]) {
149             result = YES;
150         }
151     }
152     
153     return result;
154 }
155
156 - (void)addData:(NSData *)data allAtOnce:(BOOL)allAtOnce
157 {
158     [super addData:data allAtOnce:allAtOnce];
159     [frameLoader _receivedData:data];
160 }
161
162 - (void)saveResource
163 {
164     // Override. We don't want to save the main resource as a subresource of the data source.
165 }
166
167 - (NSURLRequest *)willSendRequest:(NSURLRequest *)newRequest redirectResponse:(NSURLResponse *)redirectResponse
168 {
169     // Note that there are no asserts here as there are for the other callbacks. This is due to the
170     // fact that this "callback" is sent when starting every load, and the state of callback
171     // deferrals plays less of a part in this function in preventing the bad behavior deferring 
172     // callbacks is meant to prevent.
173     ASSERT(newRequest != nil);
174
175     // retain/release self in this delegate method since the additional processing can do
176     // anything including possibly releasing self; one example of this is 3266216
177     [self retain];
178
179     NSURL *URL = [newRequest URL];
180
181     NSMutableURLRequest *mutableRequest = nil;
182     // Update cookie policy base URL as URL changes, except for subframes, which use the
183     // URL of the main frame which doesn't change when we redirect.
184     if ([frameLoader isLoadingMainFrame]) {
185         mutableRequest = [newRequest mutableCopy];
186         [mutableRequest setMainDocumentURL:URL];
187     }
188
189     // If we're fielding a redirect in response to a POST, force a load from origin, since
190     // this is a common site technique to return to a page viewing some data that the POST
191     // just modified.
192     // Also, POST requests always load from origin, but this does not affect subresources.
193     if ([newRequest cachePolicy] == NSURLRequestUseProtocolCachePolicy && 
194         [self _isPostOrRedirectAfterPost:newRequest redirectResponse:redirectResponse]) {
195         if (!mutableRequest) {
196             mutableRequest = [newRequest mutableCopy];
197         }
198         [mutableRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
199     }
200     if (mutableRequest) {
201         newRequest = [mutableRequest autorelease];
202     }
203
204     // Note super will make a copy for us, so reassigning newRequest is important. Since we are returning this value, but
205     // it's only guaranteed to be retained by self, and self might be dealloc'ed in this method, we have to autorelease.
206     // See 3777253 for an example.
207     newRequest = [[[super willSendRequest:newRequest redirectResponse:redirectResponse] retain] autorelease];
208
209     // Don't set this on the first request.  It is set
210     // when the main load was started.
211     [frameLoader _setRequest:newRequest];
212     
213     [[frameLoader webFrame] _checkNavigationPolicyForRequest:newRequest
214                                                   dataSource:[frameLoader activeDataSource]
215                                                    formState:nil
216                                                      andCall:self
217                                                 withSelector:@selector(continueAfterNavigationPolicy:formState:)];
218
219     [self release];
220     return newRequest;
221 }
222
223 static BOOL isCaseInsensitiveEqual(NSString *a, NSString *b)
224 {
225     return [a caseInsensitiveCompare:b] == NSOrderedSame;
226 }
227
228 #define URL_BYTES_BUFFER_LENGTH 2048
229
230 static BOOL shouldLoadAsEmptyDocument(NSURL *url)
231 {
232     UInt8 *buffer = (UInt8 *)malloc(URL_BYTES_BUFFER_LENGTH);
233     CFIndex bytesFilled = CFURLGetBytes((CFURLRef)url, buffer, URL_BYTES_BUFFER_LENGTH);
234     if (bytesFilled == -1) {
235         CFIndex bytesToAllocate = CFURLGetBytes((CFURLRef)url, NULL, 0);
236         buffer = (UInt8 *)realloc(buffer, bytesToAllocate);
237         bytesFilled = CFURLGetBytes((CFURLRef)url, buffer, bytesToAllocate);
238         ASSERT(bytesFilled == bytesToAllocate);
239     }
240     
241     BOOL result = (bytesFilled == 0) || (bytesFilled > 5 && strncmp((char *)buffer, "about:", 6) == 0);
242     free(buffer);
243
244     return result;
245 }
246
247 -(void)continueAfterContentPolicy:(WebPolicyAction)contentPolicy response:(NSURLResponse *)r
248 {
249     NSURL *URL = [request URL];
250     NSString *MIMEType = [r MIMEType]; 
251     
252     switch (contentPolicy) {
253     case WebPolicyUse:
254     {
255         // Prevent remote web archives from loading because they can claim to be from any domain and thus avoid cross-domain security checks (4120255).
256         BOOL isRemote = ![URL isFileURL] && ![WebDataProtocol _webIsDataProtocolURL:URL];
257         BOOL isRemoteWebArchive = isRemote && isCaseInsensitiveEqual(@"application/x-webarchive", MIMEType);
258         if (![WebFrameLoader _canShowMIMEType:MIMEType] || isRemoteWebArchive) {
259             [frameLoader cannotShowMIMETypeForURL:URL];
260             // Check reachedTerminalState since the load may have already been cancelled inside of _handleUnimplementablePolicyWithErrorCode::.
261             if (!reachedTerminalState) {
262                 [self stopLoadingForPolicyChange];
263             }
264             return;
265         }
266         break;
267     }
268     case WebPolicyDownload:
269         [proxy setDelegate:nil];
270         [frameLoader _downloadWithLoadingConnection:connection request:request response:r proxy:proxy];
271         [proxy release];
272         proxy = nil;
273
274         [self receivedError:[self interruptForPolicyChangeError]];
275         return;
276
277     case WebPolicyIgnore:
278         [self stopLoadingForPolicyChange];
279         return;
280     
281     default:
282         ASSERT_NOT_REACHED();
283     }
284
285     [self retain];
286
287     if ([r isKindOfClass:[NSHTTPURLResponse class]]) {
288         int status = [(NSHTTPURLResponse *)r statusCode];
289         if (status < 200 || status >= 300) {
290             BOOL hostedByObject = [frameLoader isHostedByObjectElement];
291
292             [frameLoader _handleFallbackContent];
293             // object elements are no longer rendered after we fallback, so don't
294             // keep trying to process data from their load
295
296             if (hostedByObject)
297                 [self cancel];
298         }
299     }
300
301     // we may have cancelled this load as part of switching to fallback content
302     if (!reachedTerminalState) {
303         [super didReceiveResponse:r];
304     }
305
306     if (![frameLoader _isStopping] && (shouldLoadAsEmptyDocument(URL) || [WebFrameLoader _representationExistsForURLScheme:[URL scheme]])) {
307         [self didFinishLoading];
308     }
309     
310     [self release];
311 }
312
313 -(void)continueAfterContentPolicy:(WebPolicyAction)policy
314 {
315     NSURLResponse *r = [policyResponse retain];
316     BOOL isStopping = [frameLoader _isStopping];
317
318     [self cancelContentPolicy];
319     if (!isStopping)
320         [self continueAfterContentPolicy:policy response:r];
321
322     [r release];
323 }
324
325 -(void)checkContentPolicyForResponse:(NSURLResponse *)r
326 {
327     WebPolicyDecisionListener *l = [[WebPolicyDecisionListener alloc]
328                                        _initWithTarget:self action:@selector(continueAfterContentPolicy:)];
329     listener = l;
330     policyResponse = [r retain];
331
332     [l retain];
333     [frameLoader _decidePolicyForMIMEType:[r MIMEType] decisionListener:listener];
334     [l release];
335 }
336
337
338 - (void)didReceiveResponse:(NSURLResponse *)r
339 {
340     ASSERT(shouldLoadAsEmptyDocument([r URL]) || ![self defersCallbacks]);
341     ASSERT(shouldLoadAsEmptyDocument([r URL]) || ![frameLoader _defersCallbacks]);
342
343     if (loadingMultipartContent) {
344         [frameLoader _setupForReplaceByMIMEType:[r MIMEType]];
345         [self clearResourceData];
346     }
347     
348     if ([[r MIMEType] isEqualToString:@"multipart/x-mixed-replace"])
349         loadingMultipartContent = YES;
350         
351     // retain/release self in this delegate method since the additional processing can do
352     // anything including possibly releasing self; one example of this is 3266216
353     [self retain];
354     [frameLoader _setResponse:r];
355     _contentLength = [r expectedContentLength];
356
357     [self checkContentPolicyForResponse:r];
358     [self release];
359 }
360
361 - (void)didReceiveData:(NSData *)data lengthReceived:(long long)lengthReceived allAtOnce:(BOOL)allAtOnce
362 {
363     ASSERT(data);
364     ASSERT([data length] != 0);
365     ASSERT(![self defersCallbacks]);
366     ASSERT(![frameLoader _defersCallbacks]);
367  
368     // retain/release self in this delegate method since the additional processing can do
369     // anything including possibly releasing self; one example of this is 3266216
370     [self retain];
371     [frameLoader _mainReceivedBytesSoFar:_bytesReceived complete:NO];
372     
373     [super didReceiveData:data lengthReceived:lengthReceived allAtOnce:allAtOnce];
374     _bytesReceived += [data length];
375
376     [self release];
377 }
378
379 - (void)didFinishLoading
380 {
381     ASSERT(shouldLoadAsEmptyDocument([frameLoader _URL]) || ![self defersCallbacks]);
382     ASSERT(shouldLoadAsEmptyDocument([frameLoader _URL]) || ![frameLoader _defersCallbacks]);
383
384     // Calls in this method will most likely result in a call to release, so we must retain.
385     [self retain];
386
387     [frameLoader _finishedLoading];
388     [frameLoader _mainReceivedBytesSoFar:_bytesReceived complete:YES];
389     [super didFinishLoading];
390
391     [self release];
392 }
393
394 - (void)didFailWithError:(NSError *)error
395 {
396     ASSERT(![self defersCallbacks]);
397     ASSERT(![frameLoader _defersCallbacks]);
398
399     [self receivedError:error];
400 }
401
402 - (NSURLRequest *)loadWithRequestNow:(NSURLRequest *)r
403 {
404     BOOL shouldLoadEmptyBeforeRedirect = shouldLoadAsEmptyDocument([r URL]);
405
406     ASSERT(connection == nil);
407     ASSERT(shouldLoadEmptyBeforeRedirect || ![self defersCallbacks]);
408     ASSERT(shouldLoadEmptyBeforeRedirect || ![frameLoader _defersCallbacks]);
409
410     // Send this synthetic delegate callback since clients expect it, and
411     // we no longer send the callback from within NSURLConnection for
412     // initial requests.
413     r = [self willSendRequest:r redirectResponse:nil];
414     NSURL *URL = [r URL];
415     BOOL shouldLoadEmpty = shouldLoadAsEmptyDocument(URL);
416
417     if (shouldLoadEmptyBeforeRedirect && !shouldLoadEmpty && [self defersCallbacks]) {
418         return r;
419     }
420
421     if (shouldLoadEmpty || [WebFrameLoader _representationExistsForURLScheme:[URL scheme]]) {
422         NSString *MIMEType;
423         if (shouldLoadEmpty) {
424             MIMEType = @"text/html";
425         } else {
426             MIMEType = [WebFrameLoader _generatedMIMETypeForURLScheme:[URL scheme]];
427         }
428
429         NSURLResponse *resp = [[NSURLResponse alloc] initWithURL:URL MIMEType:MIMEType
430             expectedContentLength:0 textEncodingName:nil];
431         [self didReceiveResponse:resp];
432         [resp release];
433     } else {
434         connection = [[NSURLConnection alloc] initWithRequest:r delegate:proxy];
435     }
436
437     return nil;
438 }
439
440 - (BOOL)loadWithRequest:(NSURLRequest *)r
441 {
442     ASSERT(connection == nil);
443
444     BOOL defer = [self defersCallbacks];
445     if (defer) {
446         NSURL *URL = [r URL];
447         BOOL shouldLoadEmpty = shouldLoadAsEmptyDocument(URL);
448         if (shouldLoadEmpty) {
449             defer = NO;
450         }
451     }
452     if (!defer) {
453         r = [self loadWithRequestNow:r];
454         if (r != nil) {
455             // Started as an empty document, but was redirected to something non-empty.
456             ASSERT([self defersCallbacks]);
457             defer = YES;
458         }
459     }
460     if (defer) {
461         NSURLRequest *copy = [r copy];
462         [_initialRequest release];
463         _initialRequest = copy;
464     }
465
466     return YES;
467 }
468
469 - (void)setDefersCallbacks:(BOOL)defers
470 {
471     [super setDefersCallbacks:defers];
472     if (!defers) {
473         NSURLRequest *r = _initialRequest;
474         if (r != nil) {
475             _initialRequest = nil;
476             [self loadWithRequestNow:r];
477             [r release];
478         }
479     }
480 }
481
482 @end