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