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