WebKit:
[WebKit-https.git] / WebKit / mac / WebView / WebResource.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 Apple 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 "WebResourceInternal.h"
30
31 #import "WebFrameInternal.h"
32 #import "WebKitLogging.h"
33 #import "WebKitVersionChecks.h"
34 #import "WebNSDictionaryExtras.h"
35 #import "WebNSObjectExtras.h"
36 #import "WebNSURLExtras.h"
37 #import <JavaScriptCore/InitializeThreading.h>
38 #import <JavaScriptCore/PassRefPtr.h>
39 #import <WebCore/ArchiveResource.h>
40 #import <WebCore/LegacyWebArchive.h>
41 #import <WebCore/TextEncoding.h>
42 #import <WebCore/ThreadCheck.h>
43 #import <WebCore/WebCoreObjCExtras.h>
44 #import <WebCore/WebCoreURLResponse.h>
45
46 using namespace WebCore;
47
48 static NSString * const WebResourceDataKey =              @"WebResourceData";
49 static NSString * const WebResourceFrameNameKey =         @"WebResourceFrameName";
50 static NSString * const WebResourceMIMETypeKey =          @"WebResourceMIMEType";
51 static NSString * const WebResourceURLKey =               @"WebResourceURL";
52 static NSString * const WebResourceTextEncodingNameKey =  @"WebResourceTextEncodingName";
53 static NSString * const WebResourceResponseKey =          @"WebResourceResponse";
54
55 @interface WebResourcePrivate : NSObject {
56 @public
57     ArchiveResource* coreResource;
58 }
59 - (id)initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource;
60 @end
61
62 @implementation WebResourcePrivate
63
64 + (void)initialize
65 {
66     JSC::initializeThreading();
67 #ifndef BUILDING_ON_TIGER
68     WebCoreObjCFinalizeOnMainThread(self);
69 #endif
70 }
71
72 - (id)init
73 {
74     return [super init];
75 }
76
77 - (id)initWithCoreResource:(PassRefPtr<ArchiveResource>)passedResource
78 {
79     self = [super init];
80     if (!self)
81         return nil;
82     // Acquire the PassRefPtr<>'s ref as our own manual ref
83     coreResource = passedResource.releaseRef();
84     return self;
85 }
86
87 - (void)dealloc
88 {
89     if (WebCoreObjCScheduleDeallocateOnMainThread([WebResourcePrivate class], self))
90         return;
91
92     if (coreResource)
93         coreResource->deref();
94     [super dealloc];
95 }
96
97 - (void)finalize
98 {
99     if (coreResource)
100         coreResource->deref();
101     [super finalize];
102 }
103
104 @end
105
106 @implementation WebResource
107
108 - (id)init
109 {
110     self = [super init];
111     if (!self)
112         return nil;
113     _private = [[WebResourcePrivate alloc] init];
114     return self;
115 }
116
117 - (id)initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName
118 {
119     return [self _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:nil copyData:YES];
120 }
121
122 - (id)initWithCoder:(NSCoder *)decoder
123 {
124     WebCoreThreadViolationCheck();
125
126     self = [super init];
127     if (!self)
128         return nil;
129
130     NSData *data = nil;
131     NSURL *url = nil;
132     NSString *mimeType = nil, *textEncoding = nil, *frameName = nil;
133     NSURLResponse *response = nil;
134     
135     @try {
136         id object = [decoder decodeObjectForKey:WebResourceDataKey];
137         if ([object isKindOfClass:[NSData class]])
138             data = object;
139         object = [decoder decodeObjectForKey:WebResourceURLKey];
140         if ([object isKindOfClass:[NSURL class]])
141             url = object;
142         object = [decoder decodeObjectForKey:WebResourceMIMETypeKey];
143         if ([object isKindOfClass:[NSString class]])
144             mimeType = object;
145         object = [decoder decodeObjectForKey:WebResourceTextEncodingNameKey];
146         if ([object isKindOfClass:[NSString class]])
147             textEncoding = object;
148         object = [decoder decodeObjectForKey:WebResourceFrameNameKey];
149         if ([object isKindOfClass:[NSString class]])
150             frameName = object;
151         object = [decoder decodeObjectForKey:WebResourceResponseKey];
152         if ([object isKindOfClass:[NSURLResponse class]])
153             response = object;
154     } @catch(id) {
155         [self release];
156         return nil;
157     }
158
159     _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(data), url, mimeType, textEncoding, frameName, response)];
160
161     return self;
162 }
163
164 - (void)encodeWithCoder:(NSCoder *)encoder
165 {
166     ArchiveResource *resource = _private->coreResource;
167     
168     NSData *data = nil;
169     NSURL *url = nil;
170     NSString *mimeType = nil, *textEncoding = nil, *frameName = nil;
171     NSURLResponse *response = nil;
172     
173     if (resource) {
174         if (resource->data())
175             data = [resource->data()->createNSData() autorelease];
176         url = resource->url();
177         mimeType = resource->mimeType();
178         textEncoding = resource->textEncoding();
179         frameName = resource->frameName();
180         response = resource->response().nsURLResponse();
181     }
182     [encoder encodeObject:data forKey:WebResourceDataKey];
183     [encoder encodeObject:url forKey:WebResourceURLKey];
184     [encoder encodeObject:mimeType forKey:WebResourceMIMETypeKey];
185     [encoder encodeObject:textEncoding forKey:WebResourceTextEncodingNameKey];
186     [encoder encodeObject:frameName forKey:WebResourceFrameNameKey];
187     [encoder encodeObject:response forKey:WebResourceResponseKey];
188 }
189
190 - (void)dealloc
191 {
192     [_private release];
193     [super dealloc];
194 }
195
196 - (id)copyWithZone:(NSZone *)zone
197 {
198     return [self retain];
199 }
200
201 - (NSData *)data
202 {
203 #ifdef MAIL_THREAD_WORKAROUND
204     if (needMailThreadWorkaround())
205         return [self _webkit_getPropertyOnMainThread:_cmd];
206 #endif
207
208     WebCoreThreadViolationCheck();
209
210     if (!_private->coreResource)
211         return nil;
212     if (!_private->coreResource->data())
213         return nil;
214     return [_private->coreResource->data()->createNSData() autorelease];
215 }
216
217 - (NSURL *)URL
218 {
219 #ifdef MAIL_THREAD_WORKAROUND
220     if (needMailThreadWorkaround())
221         return [self _webkit_getPropertyOnMainThread:_cmd];
222 #endif
223
224     WebCoreThreadViolationCheck();
225
226     if (!_private->coreResource)
227         return nil;
228     NSURL *url = _private->coreResource->url();
229     return url;
230 }
231
232 - (NSString *)MIMEType
233 {
234 #ifdef MAIL_THREAD_WORKAROUND
235     if (needMailThreadWorkaround())
236         return [self _webkit_getPropertyOnMainThread:_cmd];
237 #endif
238
239     WebCoreThreadViolationCheck();
240
241     if (!_private->coreResource)
242         return nil;
243     NSString *mimeType = _private->coreResource->mimeType();
244     return mimeType;
245 }
246
247 - (NSString *)textEncodingName
248 {
249 #ifdef MAIL_THREAD_WORKAROUND
250     if (needMailThreadWorkaround())
251         return [self _webkit_getPropertyOnMainThread:_cmd];
252 #endif
253
254     WebCoreThreadViolationCheck();
255
256     if (!_private->coreResource)
257         return nil;
258     NSString *textEncodingName = _private->coreResource->textEncoding();
259     return textEncodingName;
260 }
261
262 - (NSString *)frameName
263 {
264 #ifdef MAIL_THREAD_WORKAROUND
265     if (needMailThreadWorkaround())
266         return [self _webkit_getPropertyOnMainThread:_cmd];
267 #endif
268
269     WebCoreThreadViolationCheck();
270
271     if (!_private->coreResource)
272         return nil;
273     NSString *frameName = _private->coreResource->frameName();
274     return frameName;
275 }
276
277 - (id)description
278 {
279     return [NSString stringWithFormat:@"<%@ %@>", [self className], [self URL]];
280 }
281
282 @end
283
284 @implementation WebResource (WebResourceInternal)
285
286 - (id)_initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource
287 {
288     self = [super init];
289     if (!self)
290         return nil;
291             
292     ASSERT(coreResource);
293     
294     // WebResources should not be init'ed with nil data, and doing so breaks certain uses of NSHTMLReader
295     // See <rdar://problem/5820157> for more info
296     if (!coreResource->data()) {
297         [self release];
298         return nil;
299     }
300     
301     _private = [[WebResourcePrivate alloc] initWithCoreResource:coreResource];
302             
303     return self;
304 }
305
306 - (WebCore::ArchiveResource *)_coreResource
307 {
308     return _private->coreResource;
309 }
310
311 @end
312
313 @implementation WebResource (WebResourcePrivate)
314
315 // SPI for Mail (5066325)
316 // FIXME: This "ignoreWhenUnarchiving" concept is an ugly one - can we find a cleaner solution for those who need this SPI?
317 - (void)_ignoreWhenUnarchiving
318 {
319 #ifdef MAIL_THREAD_WORKAROUND
320     if (needMailThreadWorkaround()) {
321         [self performSelectorOnMainThread:@selector(_ignoreWhenUnarchiving) withObject:nil waitUntilDone:TRUE];
322         return;
323     }
324 #endif
325
326     WebCoreThreadViolationCheck();
327
328     if (!_private->coreResource)
329         return;
330     _private->coreResource->ignoreWhenUnarchiving();
331 }
332
333 - (id)_initWithData:(NSData *)data 
334                 URL:(NSURL *)URL 
335            MIMEType:(NSString *)MIMEType 
336    textEncodingName:(NSString *)textEncodingName 
337           frameName:(NSString *)frameName 
338            response:(NSURLResponse *)response
339            copyData:(BOOL)copyData
340 {
341 #ifdef MAIL_THREAD_WORKAROUND
342     if (needMailThreadWorkaround()) {
343         // Maybe this could be done more cleanly with NSInvocation.
344         NSMutableDictionary *arguments = [[NSMutableDictionary alloc] init];
345         if (data)
346             [arguments setObject:data forKey:@"data"];
347         if (URL)
348             [arguments setObject:URL forKey:@"URL"];
349         if (MIMEType)
350             [arguments setObject:MIMEType forKey:@"MIMEType"];
351         if (textEncodingName)
352             [arguments setObject:textEncodingName forKey:@"textEncodingName"];
353         if (frameName)
354             [arguments setObject:frameName forKey:@"frameName"];
355         if (response)
356             [arguments setObject:response forKey:@"response"];
357         if (copyData)
358             [arguments setObject:[NSNumber numberWithBool:YES] forKey:@"copyData"];
359         [self performSelectorOnMainThread:@selector(_initWithArguments:) withObject:arguments waitUntilDone:TRUE];
360         NSException *exception = [[[arguments objectForKey:@"exception"] retain] autorelease];
361         id result = [[[arguments objectForKey:@"result"] retain] autorelease];
362         [arguments release];
363         if (exception)
364             [exception raise];
365         return result;
366     }
367 #endif
368
369     WebCoreThreadViolationCheck();
370
371     self = [super init];
372     if (!self)
373         return nil;
374     
375     if (!data || !URL || !MIMEType) {
376         [self release];
377         return nil;
378     }
379             
380     _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(copyData ? [[data copy] autorelease] : data), URL, MIMEType, textEncodingName, frameName, response)];
381             
382     return self;
383 }
384
385 - (id)_initWithData:(NSData *)data URL:(NSURL *)URL response:(NSURLResponse *)response
386 {
387     // Pass NO for copyData since the data doesn't need to be copied since we know that callers will no longer modify it.
388     // Copying it will also cause a performance regression.
389     return [self _initWithData:data
390                            URL:URL
391                       MIMEType:[response _webcore_MIMEType]
392               textEncodingName:[response textEncodingName]
393                      frameName:nil
394                       response:response
395                       copyData:NO];    
396 }
397
398 - (NSString *)_suggestedFilename
399 {
400 #ifdef MAIL_THREAD_WORKAROUND
401     if (needMailThreadWorkaround())
402         return [self _webkit_getPropertyOnMainThread:_cmd];
403 #endif
404
405     WebCoreThreadViolationCheck();
406
407     if (!_private->coreResource)
408         return nil;
409     NSString *suggestedFilename = _private->coreResource->response().suggestedFilename();
410     return suggestedFilename;
411 }
412
413 - (NSFileWrapper *)_fileWrapperRepresentation
414 {
415     NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[self data]] autorelease];
416     NSString *filename = [self _suggestedFilename];
417     if (!filename || ![filename length])
418         filename = [[self URL] _webkit_suggestedFilenameWithMIMEType:[self MIMEType]];
419     [wrapper setPreferredFilename:filename];
420     return wrapper;
421 }
422
423 - (NSURLResponse *)_response
424 {
425 #ifdef MAIL_THREAD_WORKAROUND
426     if (needMailThreadWorkaround())
427         return [self _webkit_getPropertyOnMainThread:_cmd];
428 #endif
429
430     WebCoreThreadViolationCheck();
431
432     NSURLResponse *response = nil;
433     if (_private->coreResource)
434         response = _private->coreResource->response().nsURLResponse();
435     return response ? response : [[[NSURLResponse alloc] init] autorelease];        
436 }
437
438 - (NSString *)_stringValue
439 {
440 #ifdef MAIL_THREAD_WORKAROUND
441     if (needMailThreadWorkaround())
442         return [self _webkit_getPropertyOnMainThread:_cmd];
443 #endif
444
445     WebCoreThreadViolationCheck();
446
447     WebCore::TextEncoding encoding;
448     if (_private->coreResource)
449         encoding = _private->coreResource->textEncoding();
450     if (!encoding.isValid())
451         encoding = WindowsLatin1Encoding();
452     
453     SharedBuffer* coreData = _private->coreResource ? _private->coreResource->data() : 0;
454     return encoding.decode(reinterpret_cast<const char*>(coreData ? coreData->data() : 0), coreData ? coreData->size() : 0);
455 }
456
457 @end
458
459 #ifdef MAIL_THREAD_WORKAROUND
460
461 @implementation WebResource (WebMailThreadWorkaround)
462
463 + (BOOL)_needMailThreadWorkaroundIfCalledOffMainThread
464 {
465     static BOOL isOldMail = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_MAIL_THREAD_WORKAROUND)
466         && [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.apple.mail"];
467     return isOldMail;
468 }
469
470 - (void)_initWithArguments:(NSMutableDictionary *)arguments
471 {
472     NSData *data = [arguments objectForKey:@"data"];
473     NSURL *URL = [arguments objectForKey:@"URL"];
474     NSString *MIMEType = [arguments objectForKey:@"MIMEType"];
475     NSString *textEncodingName = [arguments objectForKey:@"textEncodingName"];
476     NSString *frameName = [arguments objectForKey:@"frameName"];
477     NSURLResponse *response = [arguments objectForKey:@"response"];
478     bool copyData = [arguments objectForKey:@"copyData"];
479     @try {
480         id result = [self _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:response copyData:copyData];
481         if (result)
482             [arguments setObject:result forKey:@"result"];
483     } @catch(NSException *exception) {
484         [arguments setObject:exception forKey:@"exception"];
485     }
486 }
487
488 @end
489
490 #endif