WebArchive.mm revision 8e35f3cfc7fba1d1c829dc557ebad6409cbe16a2
1/*
2 * Copyright (C) 2005, 2006, 2007 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 "WebArchive.h"
30#import "WebArchiveInternal.h"
31
32#import "WebKitLogging.h"
33#import "WebResourceInternal.h"
34#import "WebResourcePrivate.h"
35#import "WebTypesInternal.h"
36
37#import <WebCore/ArchiveResource.h>
38#import <WebCore/LegacyWebArchive.h>
39#import <WebCore/WebCoreObjCExtras.h>
40
41using namespace WebCore;
42
43NSString *WebArchivePboardType = @"Apple Web Archive pasteboard type";
44
45static NSString * const WebMainResourceKey = @"WebMainResource";
46static NSString * const WebSubresourcesKey = @"WebSubresources";
47static NSString * const WebSubframeArchivesKey = @"WebSubframeArchives";
48
49@interface WebArchivePrivate : NSObject
50{
51@public
52    WebResource *cachedMainResource;
53    NSArray *cachedSubresources;
54    NSArray *cachedSubframeArchives;
55@private
56    LegacyWebArchive* coreArchive;
57}
58
59- (id)initWithCoreArchive:(PassRefPtr<LegacyWebArchive>)coreArchive;
60- (LegacyWebArchive*)coreArchive;
61- (void)setCoreArchive:(PassRefPtr<LegacyWebArchive>)newCoreArchive;
62@end
63
64@implementation WebArchivePrivate
65
66#ifndef BUILDING_ON_TIGER
67+ (void)initialize
68{
69    WebCoreObjCFinalizeOnMainThread(self);
70}
71#endif
72
73- (id)init
74{
75    self = [super init];
76    if (self)
77        coreArchive = LegacyWebArchive::create().releaseRef();
78    return self;
79}
80
81- (id)initWithCoreArchive:(PassRefPtr<LegacyWebArchive>)_coreArchive
82{
83    self = [super init];
84    if (!self || !_coreArchive) {
85        [self release];
86        return nil;
87    }
88
89    coreArchive = _coreArchive.releaseRef();
90
91    return self;
92}
93
94- (LegacyWebArchive*)coreArchive
95{
96    return coreArchive;
97}
98
99- (void)setCoreArchive:(PassRefPtr<LegacyWebArchive>)newCoreArchive
100{
101    ASSERT(coreArchive);
102    ASSERT(newCoreArchive);
103    coreArchive->deref();
104    coreArchive = newCoreArchive.releaseRef();
105}
106
107- (void)dealloc
108{
109    if (WebCoreObjCScheduleDeallocateOnMainThread([WebArchivePrivate class], self))
110        return;
111
112    ASSERT(coreArchive);
113    coreArchive->deref();
114    coreArchive = 0;
115
116    [cachedMainResource release];
117    [cachedSubresources release];
118    [cachedSubframeArchives release];
119
120    [super dealloc];
121}
122
123- (void)finalize
124{
125    ASSERT(coreArchive);
126    coreArchive->deref();
127    coreArchive = 0;
128
129    [super finalize];
130}
131
132@end
133
134@implementation WebArchive
135
136- (id)init
137{
138    self = [super init];
139    if (!self)
140        return nil;
141    _private = [[WebArchivePrivate alloc] init];
142    return self;
143}
144
145static BOOL isArrayOfClass(id object, Class elementClass)
146{
147    if (![object isKindOfClass:[NSArray class]])
148        return NO;
149    NSArray *array = (NSArray *)object;
150    NSUInteger count = [array count];
151    for (NSUInteger i = 0; i < count; ++i)
152        if (![[array objectAtIndex:i] isKindOfClass:elementClass])
153            return NO;
154    return YES;
155}
156
157- (id)initWithMainResource:(WebResource *)mainResource subresources:(NSArray *)subresources subframeArchives:(NSArray *)subframeArchives
158{
159    self = [super init];
160    if (!self)
161        return nil;
162
163    _private = [[WebArchivePrivate alloc] init];
164
165    _private->cachedMainResource = [mainResource retain];
166    if (!_private->cachedMainResource) {
167        [self release];
168        return nil;
169    }
170
171    if (!subresources || isArrayOfClass(subresources, [WebResource class]))
172        _private->cachedSubresources = [subresources retain];
173    else {
174        [self release];
175        return nil;
176    }
177
178    if (!subframeArchives || isArrayOfClass(subframeArchives, [WebArchive class]))
179        _private->cachedSubframeArchives = [subframeArchives retain];
180    else {
181        [self release];
182        return nil;
183    }
184
185    RefPtr<ArchiveResource> coreMainResource = mainResource ? [mainResource _coreResource] : 0;
186
187    Vector<PassRefPtr<ArchiveResource> > coreResources;
188    NSEnumerator *enumerator = [subresources objectEnumerator];
189    WebResource *subresource;
190    while ((subresource = [enumerator nextObject]) != nil)
191        coreResources.append([subresource _coreResource]);
192
193    Vector<PassRefPtr<LegacyWebArchive> > coreArchives;
194    enumerator = [subframeArchives objectEnumerator];
195    WebArchive *subframeArchive;
196    while ((subframeArchive = [enumerator nextObject]) != nil)
197        coreArchives.append([subframeArchive->_private coreArchive]);
198
199    [_private setCoreArchive:LegacyWebArchive::create(coreMainResource.release(), coreResources, coreArchives)];
200    if (![_private coreArchive]) {
201        [self release];
202        return nil;
203    }
204
205    return self;
206}
207
208- (id)initWithData:(NSData *)data
209{
210    self = [super init];
211    if (!self)
212        return nil;
213
214#if !LOG_DISABLED
215    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
216#endif
217
218    _private = [[WebArchivePrivate alloc] init];
219    [_private setCoreArchive:LegacyWebArchive::create(SharedBuffer::wrapNSData(data).get())];
220
221#if !LOG_DISABLED
222    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
223    CFAbsoluteTime duration = end - start;
224#endif
225    LOG(Timing, "Parsing web archive with [NSPropertyListSerialization propertyListFromData::::] took %f seconds", duration);
226
227    return self;
228}
229
230- (id)initWithCoder:(NSCoder *)decoder
231{
232    WebResource *mainResource = nil;
233    NSArray *subresources = nil;
234    NSArray *subframeArchives = nil;
235
236    @try {
237        id object = [decoder decodeObjectForKey:WebMainResourceKey];
238        if ([object isKindOfClass:[WebResource class]])
239            mainResource = [object retain];
240        object = [decoder decodeObjectForKey:WebSubresourcesKey];
241        if (isArrayOfClass(object, [WebResource class]))
242            subresources = [object retain];
243        object = [decoder decodeObjectForKey:WebSubframeArchivesKey];
244        if (isArrayOfClass(object, [WebArchive class]))
245            subframeArchives = [object retain];
246    } @catch(id) {
247        [self release];
248        return nil;
249    }
250
251    return [self initWithMainResource:mainResource subresources:subresources subframeArchives:subframeArchives];
252}
253
254- (void)encodeWithCoder:(NSCoder *)encoder
255{
256    [encoder encodeObject:[self mainResource] forKey:WebMainResourceKey];
257    [encoder encodeObject:[self subresources] forKey:WebSubresourcesKey];
258    [encoder encodeObject:[self subframeArchives] forKey:WebSubframeArchivesKey];
259}
260
261- (void)dealloc
262{
263    [_private release];
264    [super dealloc];
265}
266
267- (id)copyWithZone:(NSZone *)zone
268{
269    return [self retain];
270}
271
272- (WebResource *)mainResource
273{
274    // Currently from WebKit API perspective, WebArchives are entirely immutable once created
275    // If they ever become mutable, we'll need to rethink this.
276    if (!_private->cachedMainResource) {
277        LegacyWebArchive* coreArchive = [_private coreArchive];
278        if (coreArchive)
279            _private->cachedMainResource = [[WebResource alloc] _initWithCoreResource:coreArchive->mainResource()];
280    }
281
282    return [[_private->cachedMainResource retain] autorelease];
283}
284
285- (NSArray *)subresources
286{
287    // Currently from WebKit API perspective, WebArchives are entirely immutable once created
288    // If they ever become mutable, we'll need to rethink this.
289    if (!_private->cachedSubresources) {
290        LegacyWebArchive* coreArchive = [_private coreArchive];
291        if (!coreArchive)
292            _private->cachedSubresources = [[NSArray alloc] init];
293        else {
294            const Vector<RefPtr<ArchiveResource> >& subresources(coreArchive->subresources());
295            NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:subresources.size()];
296            _private->cachedSubresources = mutableArray;
297            for (unsigned i = 0; i < subresources.size(); ++i) {
298                WebResource *resource = [[WebResource alloc] _initWithCoreResource:subresources[i].get()];
299                if (resource) {
300                    [mutableArray addObject:resource];
301                    [resource release];
302                }
303            }
304        }
305    }
306
307    return [[_private->cachedSubresources retain] autorelease];
308}
309
310- (NSArray *)subframeArchives
311{
312    // Currently from WebKit API perspective, WebArchives are entirely immutable once created
313    // If they ever become mutable, we'll need to rethink this.
314    if (!_private->cachedSubframeArchives) {
315        LegacyWebArchive* coreArchive = [_private coreArchive];
316        if (!coreArchive)
317            _private->cachedSubframeArchives = [[NSArray alloc] init];
318        else {
319            const Vector<RefPtr<Archive> >& subframeArchives(coreArchive->subframeArchives());
320            NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:subframeArchives.size()];
321            _private->cachedSubframeArchives = mutableArray;
322            for (unsigned i = 0; i < subframeArchives.size(); ++i) {
323                WebArchive *archive = [[WebArchive alloc] _initWithCoreLegacyWebArchive:(LegacyWebArchive *)subframeArchives[i].get()];
324                [mutableArray addObject:archive];
325                [archive release];
326            }
327        }
328    }
329
330    return [[_private->cachedSubframeArchives retain] autorelease];
331}
332
333- (NSData *)data
334{
335#if !LOG_DISABLED
336    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
337#endif
338
339    RetainPtr<CFDataRef> data = [_private coreArchive]->rawDataRepresentation();
340
341#if !LOG_DISABLED
342    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
343    CFAbsoluteTime duration = end - start;
344#endif
345    LOG(Timing, "Serializing web archive to raw CFPropertyList data took %f seconds", duration);
346
347    return [[(NSData *)data.get() retain] autorelease];
348}
349
350@end
351
352@implementation WebArchive (WebInternal)
353
354- (id)_initWithCoreLegacyWebArchive:(PassRefPtr<WebCore::LegacyWebArchive>)coreLegacyWebArchive
355{
356    self = [super init];
357    if (!self)
358        return nil;
359
360    _private = [[WebArchivePrivate alloc] initWithCoreArchive:coreLegacyWebArchive];
361    if (!_private) {
362        [self release];
363        return nil;
364    }
365
366    return self;
367}
368
369- (WebCore::LegacyWebArchive *)_coreLegacyWebArchive
370{
371    return [_private coreArchive];
372}
373
374@end
375