2006-10-25 Anders Carlsson <acarlsson@apple.com>
[WebKit-https.git] / WebCore / loader / mac / WebSubresourceLoader.mm
1 /*
2  * Copyright (C) 2005, 2006 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 "config.h"
30 #import "WebSubresourceLoader.h"
31
32 #import "FrameLoader.h"
33 #import "FrameMac.h"
34 #import "LoaderNSURLExtras.h"
35 #import "LoaderNSURLRequestExtras.h"
36 #import "WebCoreFrameBridge.h"
37 #import "WebCoreResourceLoader.h"
38 #import "WebCoreSystemInterface.h"
39 #import "WebFormDataStream.h"
40 #import <Foundation/NSURLResponse.h>
41 #import <wtf/Assertions.h>
42
43 using namespace WebCore;
44
45 @interface WebCoreSubresourceHandle : NSObject <WebCoreResourceHandle>
46 {
47     SubresourceLoader* m_loader;
48 }
49 - (id)initWithLoader:(SubresourceLoader*)loader;
50 @end
51
52 namespace WebCore {
53
54 SubresourceLoader::SubresourceLoader(Frame* frame, id <WebCoreResourceLoader> l)
55     : WebResourceLoader(frame)
56     , m_coreLoader(l)
57     , m_loadingMultipartContent(false)
58 {
59     frameLoader()->addSubresourceLoader(this);
60 }
61
62 SubresourceLoader::~SubresourceLoader()
63 {
64 }
65
66 id <WebCoreResourceHandle> SubresourceLoader::create(Frame* frame, id <WebCoreResourceLoader> rLoader,
67     NSMutableURLRequest *newRequest, NSDictionary *customHeaders, NSString *referrer)
68 {
69     FrameLoader* fl = frame->loader();
70     if (fl->state() == WebFrameStateProvisional)
71         return nil;
72
73     wkSupportsMultipartXMixedReplace(newRequest);
74
75     NSEnumerator *e = [customHeaders keyEnumerator];
76     NSString *key;
77     while ((key = [e nextObject]))
78         [newRequest addValue:[customHeaders objectForKey:key] forHTTPHeaderField:key];
79
80     // Use the original request's cache policy for two reasons:
81     // 1. For POST requests, we mutate the cache policy for the main resource,
82     //    but we do not want this to apply to subresources
83     // 2. Delegates that modify the cache policy using willSendRequest: should
84     //    not affect any other resources. Such changes need to be done
85     //    per request.
86     if (isConditionalRequest(newRequest))
87         [newRequest setCachePolicy:NSURLRequestReloadIgnoringCacheData];
88     else
89         [newRequest setCachePolicy:[fl->originalRequest() cachePolicy]];
90     setHTTPReferrer(newRequest, referrer);
91     
92     fl->addExtraFieldsToRequest(newRequest, false, false);
93
94     RefPtr<SubresourceLoader> loader(new SubresourceLoader(frame, rLoader));
95     if (!loader->load(newRequest))
96         return nil;
97     return loader->handle();
98 }
99
100 id <WebCoreResourceHandle> SubresourceLoader::create(Frame* frame, id <WebCoreResourceLoader> rLoader,
101     NSString *method, NSURL *URL, NSDictionary *customHeaders, NSString *referrer)
102 {
103     NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] initWithURL:URL];    
104     [newRequest setHTTPMethod:method];
105     id <WebCoreResourceHandle> handle = create(frame, rLoader, newRequest, customHeaders, referrer);
106     [newRequest release];
107     return handle;
108 }
109
110 id <WebCoreResourceHandle> SubresourceLoader::create(Frame* frame, id <WebCoreResourceLoader> rLoader,
111     NSString *method, NSURL *URL, NSDictionary *customHeaders, NSArray *postData, NSString *referrer)
112 {
113     NSMutableURLRequest *newRequest = [[NSMutableURLRequest alloc] initWithURL:URL];
114     // FIXME: Because of <rdar://problem/4803505>, the method has to be set before the body.
115     // Once this is fixed we can pass the method to create like we used to.
116     [newRequest setHTTPMethod:method];
117     webSetHTTPBody(newRequest, postData);
118     id <WebCoreResourceHandle> handle = create(frame, rLoader, newRequest, customHeaders, referrer);
119     [newRequest release];
120     return handle;
121 }
122
123 NSURLRequest *SubresourceLoader::willSendRequest(NSURLRequest *newRequest, NSURLResponse *redirectResponse)
124 {
125     NSURL *oldURL = [request() URL];
126     NSURLRequest *clientRequest = WebResourceLoader::willSendRequest(newRequest, redirectResponse);
127     if (clientRequest && oldURL != [clientRequest URL] && ![oldURL isEqual:[clientRequest URL]])
128         [m_coreLoader.get() redirectedToURL:[clientRequest URL]];
129     return clientRequest;
130 }
131
132 void SubresourceLoader::didReceiveResponse(NSURLResponse *r)
133 {
134     ASSERT(r);
135
136     if ([[r MIMEType] isEqualToString:@"multipart/x-mixed-replace"])
137         m_loadingMultipartContent = true;
138
139     // Reference the object in this method since the additional processing can do
140     // anything including removing the last reference to this object; one example of this is 3266216.
141     RefPtr<SubresourceLoader> protect(this);
142
143     [m_coreLoader.get() receivedResponse:r];
144     // The coreLoader can cancel a load if it receives a multipart response for a non-image
145     if (reachedTerminalState())
146         return;
147     WebResourceLoader::didReceiveResponse(r);
148     
149     if (m_loadingMultipartContent && [resourceData() length]) {
150         // Since a subresource loader does not load multipart sections progressively,
151         // deliver the previously received data to the coreLoader all at once now.
152         // Then clear the data to make way for the next multipart section.
153         [m_coreLoader.get() addData:resourceData()];
154         clearResourceData();
155         
156         // After the first multipart section is complete, signal to delegates that this load is "finished" 
157         didFinishLoadingOnePart();
158     }
159 }
160
161 void SubresourceLoader::didReceiveData(NSData *data, long long lengthReceived, bool allAtOnce)
162 {
163     // Reference the object in this method since the additional processing can do
164     // anything including removing the last reference to this object; one example of this is 3266216.
165     RefPtr<SubresourceLoader> protect(this);
166
167     // A subresource loader does not load multipart sections progressively.
168     // So don't deliver any data to the coreLoader yet.
169     if (!m_loadingMultipartContent)
170         [m_coreLoader.get() addData:data];
171     WebResourceLoader::didReceiveData(data, lengthReceived, allAtOnce);
172 }
173
174 void SubresourceLoader::didFinishLoading()
175 {
176     if (cancelled())
177         return;
178     ASSERT(!reachedTerminalState());
179
180     // Calling removeSubresourceLoader will likely result in a call to deref, so we must protect ourselves.
181     RefPtr<SubresourceLoader> protect(this);
182
183     [m_coreLoader.get() finishWithData:resourceData()];
184     if (cancelled())
185         return;
186     frameLoader()->removeSubresourceLoader(this);
187     WebResourceLoader::didFinishLoading();
188 }
189
190 void SubresourceLoader::didFail(NSError *error)
191 {
192     if (cancelled())
193         return;
194     ASSERT(!reachedTerminalState());
195
196     // Calling removeSubresourceLoader will likely result in a call to deref, so we must protect ourselves.
197     RefPtr<SubresourceLoader> protect(this);
198
199     [m_coreLoader.get() reportError];
200     if (cancelled())
201         return;
202     frameLoader()->removeSubresourceLoader(this);
203     WebResourceLoader::didFail(error);
204 }
205
206 void SubresourceLoader::didCancel(NSError *error)
207 {
208     ASSERT(!reachedTerminalState());
209
210     // Calling removeSubresourceLoader will likely result in a call to deref, so we must protect ourselves.
211     RefPtr<SubresourceLoader> protect(this);
212
213     [m_coreLoader.get() cancel];
214     if (cancelled())
215         return;
216     frameLoader()->removeSubresourceLoader(this);
217     WebResourceLoader::didCancel(error);
218 }
219
220 id <WebCoreResourceHandle> SubresourceLoader::handle()
221 {
222     return [[[WebCoreSubresourceHandle alloc] initWithLoader:this] autorelease];
223 }
224
225 }
226
227 @implementation WebCoreSubresourceHandle
228
229 - (id)initWithLoader:(SubresourceLoader*)loader
230 {
231     self = [self init];
232     if (!self)
233         return nil;
234     loader->ref();
235     m_loader = loader;
236     return self;
237 }
238
239 - (void)dealloc
240 {
241     m_loader->deref();
242     [super dealloc];
243 }
244
245 - (void)finalize
246 {
247     m_loader->deref();
248     [super finalize];
249 }
250
251 - (void)cancel
252 {
253     m_loader->cancel();
254 }
255
256 @end