c5f9cba0e48c2397632e7aee14e8c7aa0a0472ab
[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 static 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         CachedImage *cachedImage = dynamic_cast<CachedImage *>(cachedObject);
250
251         KWQ_BLOCK_EXCEPTIONS;
252         [bridge objectLoadedFromCacheWithURL:KURL(cachedObject->url().string()).getNSURL()
253                 response:(id)cachedObject->response()
254                 size:cachedImage ? cachedImage->dataSize() : cachedObject->size()];
255         KWQ_UNBLOCK_EXCEPTIONS;
256
257         part->didTellBridgeAboutLoad(urlString);
258     }
259 }
260
261 void KWQRetainResponse(void *response)
262 {
263     // There's no way a retain can raise
264     KWQRetain((id)response);
265 }
266
267 void KWQReleaseResponse(void *response)
268 {
269     // A release could raise if it deallocs.
270     KWQ_BLOCK_EXCEPTIONS;
271     KWQRelease((id)response);
272     KWQ_UNBLOCK_EXCEPTIONS;
273 }
274
275 #define LOCAL_STRING_BUFFER_SIZE 1024
276
277 bool KWQIsResponseURLEqualToURL(void *response, const DOM::DOMString &m_url)
278 {
279     unichar _buffer[LOCAL_STRING_BUFFER_SIZE];
280     unichar *urlStringCharacters;
281     
282     NSURL *responseURL = [(NSURLResponse *)response URL];
283     NSString *urlString = [responseURL absoluteString];
284
285     if (m_url.length() != [urlString length])
286         return false;
287         
288     // Nasty hack to directly compare strings buffers of NSString
289     // and DOMString.  We do this for speed.
290     if ([urlString length] > LOCAL_STRING_BUFFER_SIZE) {
291         urlStringCharacters = (unichar *)malloc (sizeof(unichar)*[urlString length]);
292     }
293     else {
294         urlStringCharacters = _buffer;
295     }
296     [urlString getCharacters:urlStringCharacters];
297     
298     bool ret = false;
299     if(!memcmp(urlStringCharacters, m_url.unicode(), m_url.length()*sizeof(QChar)))
300         ret = true;
301     
302     if (urlStringCharacters != _buffer)
303         free (urlStringCharacters);
304         
305     return ret;
306 }
307
308 QString KWQResponseURL(void *response)
309 {
310     KWQ_BLOCK_EXCEPTIONS;
311
312     NSURL *responseURL = [(NSURLResponse *)response URL];
313     NSString *urlString = [responseURL absoluteString];
314     
315     QString string;
316     string.setBufferFromCFString((CFStringRef)urlString);
317     return string;
318
319     KWQ_UNBLOCK_EXCEPTIONS;
320     
321     return NULL;
322 }
323
324 void *KWQResponseMIMEType(void *response)
325 {
326     KWQ_BLOCK_EXCEPTIONS;
327     return [(NSURLResponse *)response MIMEType];
328     KWQ_UNBLOCK_EXCEPTIONS;
329
330     return NULL;
331 }
332
333 void *KWQResponseTextEncodingName(void *response)
334 {
335     KWQ_BLOCK_EXCEPTIONS;
336     return [(NSURLResponse *)response textEncodingName];
337     KWQ_UNBLOCK_EXCEPTIONS;
338
339     return NULL;
340 }
341
342 void *KWQResponseHeaderString(void *response)
343 {
344     KWQ_BLOCK_EXCEPTIONS;
345     NSURLResponse *nsResponse = (NSURLResponse *)response;
346     if ([nsResponse isKindOfClass:[NSHTTPURLResponse class]]) {
347         NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)nsResponse;
348         NSDictionary *headers = [httpResponse allHeaderFields];
349
350         return KWQHeaderStringFromDictionary(headers, [httpResponse statusCode]);
351     }
352
353     KWQ_UNBLOCK_EXCEPTIONS;
354
355     return NULL;
356 }
357
358 time_t KWQCacheObjectExpiresTime(khtml::DocLoader *docLoader, void *response)
359 {
360     KWQ_BLOCK_EXCEPTIONS;
361     
362     KWQKHTMLPart *part = static_cast<KWQKHTMLPart *>(docLoader->part());
363     WebCoreBridge *bridge = part->bridge();
364     return [bridge expiresTimeForResponse:(NSURLResponse *)response];
365     
366     KWQ_UNBLOCK_EXCEPTIONS;
367     
368     return 0;
369 }
370
371 KWQLoader::KWQLoader(Loader *loader)
372     : _requestStarted(loader, SIGNAL(requestStarted(khtml::DocLoader *, khtml::CachedObject *)))
373     , _requestDone(loader, SIGNAL(requestDone(khtml::DocLoader *, khtml::CachedObject *)))
374     , _requestFailed(loader, SIGNAL(requestFailed(khtml::DocLoader *, khtml::CachedObject *)))
375 {
376 }