WebCore:
[WebKit-https.git] / WebCore / kwq / KWQLoader.mm
1 /*
2  * Copyright (C) 2004 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "KWQLoader.h"
27
28 #import "KWQExceptions.h"
29 #import "KWQFormData.h"
30 #import "KWQFoundationExtras.h"
31 #import "KWQKJobClasses.h"
32 #import "KWQLogging.h"
33 #import "KWQResourceLoader.h"
34 #import "WebCoreBridge.h"
35 #import "khtml_part.h"
36 #import "loader.h"
37
38 #import <Foundation/NSURLResponse.h>
39
40 using khtml::Cache;
41 using khtml::CachedObject;
42 using khtml::CachedImage;
43 using khtml::DocLoader;
44 using khtml::Loader;
45 using khtml::Request;
46 using KIO::TransferJob;
47
48 bool KWQServeRequest(Loader *loader, Request *request, TransferJob *job)
49 {
50     LOG(Loading, "Serving request for base %s, url %s", 
51         request->m_docLoader->part()->baseURL().url().latin1(),
52         request->object->url().string().latin1());
53     
54     return KWQServeRequest(loader, request->m_docLoader, job);
55 }
56
57 @interface NSDictionary (WebCore_Extras)
58 - (id)_webcore_initWithHeaderString:(NSString *)string;
59 @end
60
61 @implementation NSDictionary (WebCore_Extras)
62 - (id)_webcore_initWithHeaderString:(NSString *)string
63 {
64     NSMutableDictionary *headers = [[NSMutableDictionary alloc] init];
65
66     NSArray *lines = [string componentsSeparatedByString:@"\r\n"];
67
68     NSEnumerator *e = [lines objectEnumerator];
69     NSString *line;
70
71     NSString *lastHeaderName = nil;
72
73     while ((line = (NSString *)[e nextObject]) != nil) {
74         if (([line characterAtIndex:0] == ' ' || [line characterAtIndex:0] == '\t')
75             && lastHeaderName != nil) {
76             // lines that start with space or tab continue the previous header value
77             NSString *oldVal = [headers objectForKey:lastHeaderName];
78             ASSERT(oldVal);
79             [headers setObject:[NSString stringWithFormat:@"%@ %@", oldVal, [line stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" \t"]]]
80                         forKey:lastHeaderName];
81             continue;
82         }
83
84         NSRange colonRange = [line rangeOfString:@":"];
85         if (colonRange.location != NSNotFound) {
86             // don't worry about case, assume lower levels will take care of it
87
88             NSString *headerName = [[line substringToIndex:colonRange.location] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" \t"]];
89             NSString *headerValue = [[line substringFromIndex:colonRange.location + 1] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" \t"]];
90             
91             NSString *oldVal = [headers objectForKey:headerName];
92             if (oldVal) {
93                 headerValue = [NSString stringWithFormat:@"%@, %@", oldVal, headerValue];
94             }
95
96             [headers setObject:headerValue forKey:headerName];
97             
98             lastHeaderName = headerName;
99         }
100     }
101
102     self = [self initWithDictionary:headers];
103     [headers release];
104     return self;
105 }
106
107 @end
108
109 bool KWQServeRequest(Loader *loader, DocLoader *docLoader, TransferJob *job)
110 {
111     KWQKHTMLPart *part = static_cast<KWQKHTMLPart *>(docLoader->part());
112     WebCoreBridge *bridge = part->bridge();
113
114     part->didTellBridgeAboutLoad(job->url().url());
115
116     KWQ_BLOCK_EXCEPTIONS;
117     KWQResourceLoader *resourceLoader = [[KWQResourceLoader alloc] initWithJob:job];
118
119     id <WebCoreResourceHandle> handle;
120
121     NSDictionary *headerDict = nil;
122     QString headerString = job->queryMetaData("customHTTPHeader");
123
124     if (!headerString.isEmpty()) {
125         headerDict = [[NSDictionary alloc] _webcore_initWithHeaderString:headerString.getNSString()];
126     }
127
128     if (job->method() == "POST") {
129         handle = [bridge startLoadingResource:resourceLoader withURL:job->url().getNSURL() customHeaders:headerDict
130             postData:arrayFromFormData(job->postData())];
131     } else {
132         handle = [bridge startLoadingResource:resourceLoader withURL:job->url().getNSURL() customHeaders:headerDict];
133     }
134     [resourceLoader setHandle:handle];
135     [resourceLoader release];
136     return handle != nil;
137     KWQ_UNBLOCK_EXCEPTIONS;
138
139     return true;
140 }
141
142 NSString *KWQHeaderStringFromDictionary(NSDictionary *headers, int statusCode)
143 {
144     NSMutableString *headerString = [[NSMutableString alloc] init];
145     [headerString appendString:[NSString stringWithFormat:@"HTTP/1.0 %d OK\n", statusCode]];
146     
147     NSEnumerator *e = [headers keyEnumerator];
148     NSString *key;
149     
150     bool first = true;
151     
152     while ((key = [e nextObject]) != nil) {
153         if (first) {
154             first = false;
155         } else {
156             [headerString appendString:@"\n"];
157         }
158         [headerString appendString:key];
159         [headerString appendString:@": "];
160         [headerString appendString:[headers objectForKey:key]];
161     }
162         
163     return headerString;
164 }
165
166 QByteArray KWQServeSynchronousRequest(Loader *loader, DocLoader *docLoader, TransferJob *job, KURL &finalURL, QString &responseHeaders)
167 {
168     KWQKHTMLPart *part = static_cast<KWQKHTMLPart *>(docLoader->part());
169     WebCoreBridge *bridge = part->bridge();
170
171     part->didTellBridgeAboutLoad(job->url().url());
172
173     KWQ_BLOCK_EXCEPTIONS;
174
175     NSDictionary *headerDict = nil;
176     QString headerString = job->queryMetaData("customHTTPHeader");
177
178     if (!headerString.isEmpty()) {
179         headerDict = [[NSDictionary alloc] _webcore_initWithHeaderString:headerString.getNSString()];
180     }
181
182     NSArray *postData = nil;
183     if (job->method() == "POST") {
184         postData = arrayFromFormData(job->postData());
185     }
186
187     NSURL *finalNSURL = nil;
188     NSDictionary *responseHeaderDict = nil;
189     int statusCode = 0;
190     NSData *resultData = [bridge syncLoadResourceWithURL:job->url().getNSURL() customHeaders:headerDict postData:postData finalURL:&finalNSURL responseHeaders:&responseHeaderDict statusCode:&statusCode];
191     [headerDict release];
192     
193     job->kill();
194
195     finalURL = finalNSURL;
196     responseHeaders = QString::fromNSString(KWQHeaderStringFromDictionary(responseHeaderDict, statusCode));
197
198     QByteArray results([resultData length]);
199
200     memcpy( results.data(), [resultData bytes], [resultData length] );
201
202     return results;
203
204     KWQ_UNBLOCK_EXCEPTIONS;
205
206     return QByteArray();
207 }
208
209 int KWQNumberOfPendingOrLoadingRequests(khtml::DocLoader *dl)
210 {
211     return Cache::loader()->numRequests(dl);
212 }
213
214 bool KWQCheckIfReloading(DocLoader *loader)
215 {
216     KWQ_BLOCK_EXCEPTIONS;
217     return [static_cast<KWQKHTMLPart *>(loader->part())->bridge() isReloading];
218     KWQ_UNBLOCK_EXCEPTIONS;
219
220     return false;
221 }
222
223 void KWQCheckCacheObjectStatus(DocLoader *loader, CachedObject *cachedObject)
224 {
225     // Return from the function for objects that we didn't load from the cache.
226     if (!cachedObject)
227         return;
228     switch (cachedObject->status()) {
229     case CachedObject::Persistent:
230     case CachedObject::Cached:
231     case CachedObject::Uncacheable:
232         break;
233     case CachedObject::NotCached:
234     case CachedObject::Unknown:
235     case CachedObject::New:
236     case CachedObject::Pending:
237         return;
238     }
239     
240     ASSERT(cachedObject->response());
241     
242     // Notify the caller that we "loaded".
243     KWQKHTMLPart *part = static_cast<KWQKHTMLPart *>(loader->part());
244
245     QString urlString = cachedObject->url().string();
246
247     if (!part->haveToldBridgeAboutLoad(urlString)) {
248         WebCoreBridge *bridge = part->bridge();
249
250         KWQ_BLOCK_EXCEPTIONS;
251         [bridge objectLoadedFromCacheWithURL:KURL(cachedObject->url().string()).getNSURL()
252                                     response:(NSURLResponse *)cachedObject->response()
253                                         data:(NSData *)cachedObject->allData()];
254         KWQ_UNBLOCK_EXCEPTIONS;
255
256         part->didTellBridgeAboutLoad(urlString);
257     }
258 }
259
260 #define LOCAL_STRING_BUFFER_SIZE 1024
261
262 bool KWQIsResponseURLEqualToURL(NSURLResponse *response, const DOM::DOMString &m_url)
263 {
264     unichar _buffer[LOCAL_STRING_BUFFER_SIZE];
265     unichar *urlStringCharacters;
266     
267     NSURL *responseURL = [(NSURLResponse *)response URL];
268     NSString *urlString = [responseURL absoluteString];
269
270     if (m_url.length() != [urlString length])
271         return false;
272         
273     // Nasty hack to directly compare strings buffers of NSString
274     // and DOMString.  We do this for speed.
275     if ([urlString length] > LOCAL_STRING_BUFFER_SIZE) {
276         urlStringCharacters = (unichar *)malloc (sizeof(unichar)*[urlString length]);
277     }
278     else {
279         urlStringCharacters = _buffer;
280     }
281     [urlString getCharacters:urlStringCharacters];
282     
283     bool ret = false;
284     if(!memcmp(urlStringCharacters, m_url.unicode(), m_url.length()*sizeof(QChar)))
285         ret = true;
286     
287     if (urlStringCharacters != _buffer)
288         free (urlStringCharacters);
289         
290     return ret;
291 }
292
293 QString KWQResponseURL(NSURLResponse *response)
294 {
295     KWQ_BLOCK_EXCEPTIONS;
296
297     NSURL *responseURL = [(NSURLResponse *)response URL];
298     NSString *urlString = [responseURL absoluteString];
299     
300     QString string;
301     string.setBufferFromCFString((CFStringRef)urlString);
302     return string;
303
304     KWQ_UNBLOCK_EXCEPTIONS;
305     
306     return NULL;
307 }
308
309 NSString *KWQResponseMIMEType(NSURLResponse *response)
310 {
311     KWQ_BLOCK_EXCEPTIONS;
312     return [(NSURLResponse *)response MIMEType];
313     KWQ_UNBLOCK_EXCEPTIONS;
314
315     return NULL;
316 }
317
318 time_t KWQCacheObjectExpiresTime(khtml::DocLoader *docLoader, NSURLResponse *response)
319 {
320     KWQ_BLOCK_EXCEPTIONS;
321     
322     KWQKHTMLPart *part = static_cast<KWQKHTMLPart *>(docLoader->part());
323     WebCoreBridge *bridge = part->bridge();
324     return [bridge expiresTimeForResponse:(NSURLResponse *)response];
325     
326     KWQ_UNBLOCK_EXCEPTIONS;
327     
328     return 0;
329 }
330
331 KWQLoader::KWQLoader(Loader *loader)
332     : _requestStarted(loader, SIGNAL(requestStarted(khtml::DocLoader *, khtml::CachedObject *)))
333     , _requestDone(loader, SIGNAL(requestDone(khtml::DocLoader *, khtml::CachedObject *)))
334     , _requestFailed(loader, SIGNAL(requestFailed(khtml::DocLoader *, khtml::CachedObject *)))
335 {
336 }
337
338 namespace khtml {
339     
340 void CachedObject::setResponse(NSURLResponse *response)
341 {
342     KWQRetain(response);
343     KWQ_BLOCK_EXCEPTIONS;
344     KWQRelease(m_response);
345     KWQ_UNBLOCK_EXCEPTIONS;
346
347     m_response = response;
348 }
349
350 void CachedObject::setAllData(NSData *allData)
351 {
352     KWQRetain(allData);
353     KWQ_BLOCK_EXCEPTIONS;
354     KWQRelease(m_allData);
355     KWQ_UNBLOCK_EXCEPTIONS;
356
357     m_allData = allData;
358 }
359
360 } // namespace