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 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 <wtf/PassRefPtr.h>
39#import <WebCore/ArchiveResource.h>
40#import <WebCore/LegacyWebArchive.h>
41#import <WebCore/RuntimeApplicationChecks.h>
42#import <WebCore/TextEncoding.h>
43#import <WebCore/ThreadCheck.h>
44#import <WebCore/WebCoreObjCExtras.h>
45#import <WebCore/WebCoreURLResponse.h>
46#import <wtf/MainThread.h>
47#import <wtf/RunLoop.h>
48
49using namespace WebCore;
50
51static NSString * const WebResourceDataKey =              @"WebResourceData";
52static NSString * const WebResourceFrameNameKey =         @"WebResourceFrameName";
53static NSString * const WebResourceMIMETypeKey =          @"WebResourceMIMEType";
54static NSString * const WebResourceURLKey =               @"WebResourceURL";
55static NSString * const WebResourceTextEncodingNameKey =  @"WebResourceTextEncodingName";
56static NSString * const WebResourceResponseKey =          @"WebResourceResponse";
57
58@interface WebResourcePrivate : NSObject {
59@public
60    ArchiveResource* coreResource;
61}
62- (instancetype)initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource;
63@end
64
65@implementation WebResourcePrivate
66
67+ (void)initialize
68{
69#if !PLATFORM(IOS)
70    JSC::initializeThreading();
71    WTF::initializeMainThreadToProcessMainThread();
72    RunLoop::initializeMainRunLoop();
73#endif
74    WebCoreObjCFinalizeOnMainThread(self);
75}
76
77- (instancetype)init
78{
79    return [super init];
80}
81
82- (instancetype)initWithCoreResource:(PassRefPtr<ArchiveResource>)passedResource
83{
84    self = [super init];
85    if (!self)
86        return nil;
87    // Acquire the PassRefPtr<>'s ref as our own manual ref
88    coreResource = passedResource.leakRef();
89    return self;
90}
91
92- (void)dealloc
93{
94    if (WebCoreObjCScheduleDeallocateOnMainThread([WebResourcePrivate class], self))
95        return;
96
97    if (coreResource)
98        coreResource->deref();
99    [super dealloc];
100}
101
102- (void)finalize
103{
104    if (coreResource)
105        coreResource->deref();
106    [super finalize];
107}
108
109@end
110
111@implementation WebResource
112
113- (instancetype)init
114{
115    self = [super init];
116    if (!self)
117        return nil;
118    _private = [[WebResourcePrivate alloc] init];
119    return self;
120}
121
122- (instancetype)initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName
123{
124    return [self _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:nil copyData:YES];
125}
126
127- (instancetype)initWithCoder:(NSCoder *)decoder
128{
129    WebCoreThreadViolationCheckRoundTwo();
130
131    self = [super init];
132    if (!self)
133        return nil;
134
135    NSData *data = nil;
136    NSURL *url = nil;
137    NSString *mimeType = nil, *textEncoding = nil, *frameName = nil;
138    NSURLResponse *response = nil;
139
140    @try {
141        id object = [decoder decodeObjectForKey:WebResourceDataKey];
142        if ([object isKindOfClass:[NSData class]])
143            data = object;
144        object = [decoder decodeObjectForKey:WebResourceURLKey];
145        if ([object isKindOfClass:[NSURL class]])
146            url = object;
147        object = [decoder decodeObjectForKey:WebResourceMIMETypeKey];
148        if ([object isKindOfClass:[NSString class]])
149            mimeType = object;
150        object = [decoder decodeObjectForKey:WebResourceTextEncodingNameKey];
151        if ([object isKindOfClass:[NSString class]])
152            textEncoding = object;
153        object = [decoder decodeObjectForKey:WebResourceFrameNameKey];
154        if ([object isKindOfClass:[NSString class]])
155            frameName = object;
156        object = [decoder decodeObjectForKey:WebResourceResponseKey];
157        if ([object isKindOfClass:[NSURLResponse class]])
158            response = object;
159    } @catch(id) {
160        [self release];
161        return nil;
162    }
163
164    _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(data), url, mimeType, textEncoding, frameName, response)];
165
166    return self;
167}
168
169- (void)encodeWithCoder:(NSCoder *)encoder
170{
171    ArchiveResource *resource = _private->coreResource;
172
173    NSData *data = nil;
174    NSURL *url = nil;
175    NSString *mimeType = nil, *textEncoding = nil, *frameName = nil;
176    NSURLResponse *response = nil;
177
178    if (resource) {
179        if (resource->data())
180            data = resource->data()->createNSData().get();
181        url = resource->url();
182        mimeType = resource->mimeType();
183        textEncoding = resource->textEncoding();
184        frameName = resource->frameName();
185        response = resource->response().nsURLResponse();
186    }
187    [encoder encodeObject:data forKey:WebResourceDataKey];
188    [encoder encodeObject:url forKey:WebResourceURLKey];
189    [encoder encodeObject:mimeType forKey:WebResourceMIMETypeKey];
190    [encoder encodeObject:textEncoding forKey:WebResourceTextEncodingNameKey];
191    [encoder encodeObject:frameName forKey:WebResourceFrameNameKey];
192    [encoder encodeObject:response forKey:WebResourceResponseKey];
193}
194
195- (void)dealloc
196{
197    [_private release];
198    [super dealloc];
199}
200
201- (id)copyWithZone:(NSZone *)zone
202{
203    return [self retain];
204}
205
206- (NSData *)data
207{
208    WebCoreThreadViolationCheckRoundTwo();
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    WebCoreThreadViolationCheckRoundTwo();
220
221    if (!_private->coreResource)
222        return nil;
223    NSURL *url = _private->coreResource->url();
224    return url;
225}
226
227- (NSString *)MIMEType
228{
229    WebCoreThreadViolationCheckRoundTwo();
230
231    if (!_private->coreResource)
232        return nil;
233    NSString *mimeType = _private->coreResource->mimeType();
234    return mimeType;
235}
236
237- (NSString *)textEncodingName
238{
239    WebCoreThreadViolationCheckRoundTwo();
240
241    if (!_private->coreResource)
242        return nil;
243    NSString *textEncodingName = _private->coreResource->textEncoding();
244    return textEncodingName;
245}
246
247- (NSString *)frameName
248{
249    WebCoreThreadViolationCheckRoundTwo();
250
251    if (!_private->coreResource)
252        return nil;
253    NSString *frameName = _private->coreResource->frameName();
254    return frameName;
255}
256
257- (NSString *)description
258{
259#if !PLATFORM(IOS)
260    return [NSString stringWithFormat:@"<%@ %@>", [self className], [self URL]];
261#else
262    return [NSString stringWithFormat:@"<%@ %@>", NSStringFromClass([self class]), [self URL]];
263#endif
264}
265
266@end
267
268@implementation WebResource (WebResourceInternal)
269
270- (id)_initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource
271{
272    self = [super init];
273    if (!self)
274        return nil;
275
276    ASSERT(coreResource);
277
278    // WebResources should not be init'ed with nil data, and doing so breaks certain uses of NSHTMLReader
279    // See <rdar://problem/5820157> for more info
280    if (!coreResource->data()) {
281        [self release];
282        return nil;
283    }
284
285    _private = [[WebResourcePrivate alloc] initWithCoreResource:coreResource];
286
287    return self;
288}
289
290- (WebCore::ArchiveResource *)_coreResource
291{
292    return _private->coreResource;
293}
294
295@end
296
297@implementation WebResource (WebResourcePrivate)
298
299// SPI for Mail (5066325)
300// FIXME: This "ignoreWhenUnarchiving" concept is an ugly one - can we find a cleaner solution for those who need this SPI?
301- (void)_ignoreWhenUnarchiving
302{
303    WebCoreThreadViolationCheckRoundTwo();
304
305    if (!_private->coreResource)
306        return;
307    _private->coreResource->ignoreWhenUnarchiving();
308}
309
310- (id)_initWithData:(NSData *)data
311                URL:(NSURL *)URL
312           MIMEType:(NSString *)MIMEType
313   textEncodingName:(NSString *)textEncodingName
314          frameName:(NSString *)frameName
315           response:(NSURLResponse *)response
316           copyData:(BOOL)copyData
317{
318    WebCoreThreadViolationCheckRoundTwo();
319
320    self = [super init];
321    if (!self)
322        return nil;
323
324    if (!data || !URL || !MIMEType) {
325        [self release];
326        return nil;
327    }
328
329    _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(copyData ? [[data copy] autorelease] : data), URL, MIMEType, textEncodingName, frameName, response)];
330
331    return self;
332}
333
334- (id)_initWithData:(NSData *)data URL:(NSURL *)URL response:(NSURLResponse *)response
335{
336    // Pass NO for copyData since the data doesn't need to be copied since we know that callers will no longer modify it.
337    // Copying it will also cause a performance regression.
338    return [self _initWithData:data
339                           URL:URL
340                      MIMEType:[response MIMEType]
341              textEncodingName:[response textEncodingName]
342                     frameName:nil
343                      response:response
344                      copyData:NO];
345}
346
347- (NSString *)_suggestedFilename
348{
349    WebCoreThreadViolationCheckRoundTwo();
350
351    if (!_private->coreResource)
352        return nil;
353    NSString *suggestedFilename = _private->coreResource->response().suggestedFilename();
354    return suggestedFilename;
355}
356
357#if !PLATFORM(IOS)
358- (NSFileWrapper *)_fileWrapperRepresentation
359{
360    NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[self data]] autorelease];
361    NSString *filename = [self _suggestedFilename];
362    if (!filename || ![filename length])
363        filename = [[self URL] _webkit_suggestedFilenameWithMIMEType:[self MIMEType]];
364    [wrapper setPreferredFilename:filename];
365    return wrapper;
366}
367#endif
368
369- (NSURLResponse *)_response
370{
371    WebCoreThreadViolationCheckRoundTwo();
372
373    NSURLResponse *response = nil;
374    if (_private->coreResource)
375        response = _private->coreResource->response().nsURLResponse();
376    return response ? response : [[[NSURLResponse alloc] init] autorelease];
377}
378
379- (NSString *)_stringValue
380{
381    WebCoreThreadViolationCheckRoundTwo();
382
383    WebCore::TextEncoding encoding;
384    if (_private->coreResource)
385        encoding = _private->coreResource->textEncoding();
386    if (!encoding.isValid())
387        encoding = WindowsLatin1Encoding();
388
389    SharedBuffer* coreData = _private->coreResource ? _private->coreResource->data() : 0;
390    return encoding.decode(reinterpret_cast<const char*>(coreData ? coreData->data() : 0), coreData ? coreData->size() : 0);
391}
392
393@end
394