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 "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#import <wtf/MainThread.h>
42#import <wtf/RunLoop.h>
43
44using namespace WebCore;
45
46NSString *WebArchivePboardType = @"Apple Web Archive pasteboard type";
47
48static NSString * const WebMainResourceKey = @"WebMainResource";
49static NSString * const WebSubresourcesKey = @"WebSubresources";
50static NSString * const WebSubframeArchivesKey = @"WebSubframeArchives";
51
52@interface WebArchivePrivate : NSObject {
53@public
54    WebResource *cachedMainResource;
55    NSArray *cachedSubresources;
56    NSArray *cachedSubframeArchives;
57@private
58    RefPtr<LegacyWebArchive> coreArchive;
59}
60
61- (instancetype)initWithCoreArchive:(PassRefPtr<LegacyWebArchive>)coreArchive;
62- (LegacyWebArchive*)coreArchive;
63- (void)setCoreArchive:(PassRefPtr<LegacyWebArchive>)newCoreArchive;
64@end
65
66@implementation WebArchivePrivate
67
68+ (void)initialize
69{
70#if !PLATFORM(IOS)
71    JSC::initializeThreading();
72    WTF::initializeMainThreadToProcessMainThread();
73    RunLoop::initializeMainRunLoop();
74#endif
75    WebCoreObjCFinalizeOnMainThread(self);
76}
77
78- (instancetype)init
79{
80    self = [super init];
81    if (!self)
82        return nil;
83    coreArchive = LegacyWebArchive::create();
84    return self;
85}
86
87- (instancetype)initWithCoreArchive:(PassRefPtr<LegacyWebArchive>)_coreArchive
88{
89    self = [super init];
90    if (!self || !_coreArchive) {
91        [self release];
92        return nil;
93    }
94    coreArchive = _coreArchive;
95    return self;
96}
97
98- (LegacyWebArchive*)coreArchive
99{
100    return coreArchive.get();
101}
102
103- (void)setCoreArchive:(PassRefPtr<LegacyWebArchive>)newCoreArchive
104{
105    ASSERT(coreArchive);
106    ASSERT(newCoreArchive);
107    coreArchive = newCoreArchive;
108}
109
110- (void)dealloc
111{
112    if (WebCoreObjCScheduleDeallocateOnMainThread([WebArchivePrivate class], self))
113        return;
114
115    [cachedMainResource release];
116    [cachedSubresources release];
117    [cachedSubframeArchives release];
118
119    [super dealloc];
120}
121
122@end
123
124@implementation WebArchive
125
126- (instancetype)init
127{
128    WebCoreThreadViolationCheckRoundTwo();
129
130    self = [super init];
131    if (!self)
132        return nil;
133    _private = [[WebArchivePrivate alloc] init];
134    return self;
135}
136
137static BOOL isArrayOfClass(id object, Class elementClass)
138{
139    if (![object isKindOfClass:[NSArray class]])
140        return NO;
141    NSArray *array = (NSArray *)object;
142    NSUInteger count = [array count];
143    for (NSUInteger i = 0; i < count; ++i)
144        if (![[array objectAtIndex:i] isKindOfClass:elementClass])
145            return NO;
146    return YES;
147}
148
149- (instancetype)initWithMainResource:(WebResource *)mainResource subresources:(NSArray *)subresources subframeArchives:(NSArray *)subframeArchives
150{
151    WebCoreThreadViolationCheckRoundTwo();
152
153    self = [super init];
154    if (!self)
155        return nil;
156
157    _private = [[WebArchivePrivate alloc] init];
158
159    _private->cachedMainResource = [mainResource retain];
160    if (!_private->cachedMainResource) {
161        [self release];
162        return nil;
163    }
164
165    if (!subresources || isArrayOfClass(subresources, [WebResource class]))
166        _private->cachedSubresources = [subresources retain];
167    else {
168        [self release];
169        return nil;
170    }
171
172    if (!subframeArchives || isArrayOfClass(subframeArchives, [WebArchive class]))
173        _private->cachedSubframeArchives = [subframeArchives retain];
174    else {
175        [self release];
176        return nil;
177    }
178
179    RefPtr<ArchiveResource> coreMainResource = mainResource ? [mainResource _coreResource] : 0;
180
181    Vector<RefPtr<ArchiveResource>> coreResources;
182    for (WebResource *subresource in subresources)
183        coreResources.append([subresource _coreResource]);
184
185    Vector<RefPtr<LegacyWebArchive>> coreArchives;
186    for (WebArchive *subframeArchive in subframeArchives)
187        coreArchives.append([subframeArchive->_private coreArchive]);
188
189    RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::create(coreMainResource.release(), WTF::move(coreResources), WTF::move(coreArchives));
190    if (!coreArchive) {
191        [self release];
192        return nil;
193    }
194
195    [_private setCoreArchive:coreArchive.release()];
196
197    return self;
198}
199
200- (instancetype)initWithData:(NSData *)data
201{
202    WebCoreThreadViolationCheckRoundTwo();
203
204    self = [super init];
205    if (!self)
206        return nil;
207
208#if !LOG_DISABLED
209    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
210#endif
211
212    _private = [[WebArchivePrivate alloc] init];
213    RefPtr<LegacyWebArchive> coreArchive = LegacyWebArchive::create(SharedBuffer::wrapNSData(data).get());
214    if (!coreArchive) {
215        [self release];
216        return nil;
217    }
218
219    [_private setCoreArchive:coreArchive.release()];
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- (instancetype)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;
240        object = [decoder decodeObjectForKey:WebSubresourcesKey];
241        if (isArrayOfClass(object, [WebResource class]))
242            subresources = object;
243        object = [decoder decodeObjectForKey:WebSubframeArchivesKey];
244        if (isArrayOfClass(object, [WebArchive class]))
245            subframeArchives = object;
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    WebCoreThreadViolationCheckRoundTwo();
275
276    // Currently from WebKit API perspective, WebArchives are entirely immutable once created
277    // If they ever become mutable, we'll need to rethink this.
278    if (!_private->cachedMainResource) {
279        LegacyWebArchive* coreArchive = [_private coreArchive];
280        if (coreArchive)
281            _private->cachedMainResource = [[WebResource alloc] _initWithCoreResource:coreArchive->mainResource()];
282    }
283
284    return [[_private->cachedMainResource retain] autorelease];
285}
286
287- (NSArray *)subresources
288{
289    WebCoreThreadViolationCheckRoundTwo();
290
291    // Currently from WebKit API perspective, WebArchives are entirely immutable once created
292    // If they ever become mutable, we'll need to rethink this.
293    if (!_private->cachedSubresources) {
294        LegacyWebArchive* coreArchive = [_private coreArchive];
295        if (!coreArchive)
296            _private->cachedSubresources = [[NSArray alloc] init];
297        else {
298            const Vector<RefPtr<ArchiveResource>>& subresources(coreArchive->subresources());
299            NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:subresources.size()];
300            _private->cachedSubresources = mutableArray;
301            for (unsigned i = 0; i < subresources.size(); ++i) {
302                WebResource *resource = [[WebResource alloc] _initWithCoreResource:subresources[i].get()];
303                if (resource) {
304                    [mutableArray addObject:resource];
305                    [resource release];
306                }
307            }
308        }
309    }
310    // Maintain the WebKit 3 behavior of this API, which is documented and
311    // relied upon by some clients, of returning nil if there are no subresources.
312    return [_private->cachedSubresources count] ? [[_private->cachedSubresources retain] autorelease] : nil;
313}
314
315- (NSArray *)subframeArchives
316{
317    WebCoreThreadViolationCheckRoundTwo();
318
319    // Currently from WebKit API perspective, WebArchives are entirely immutable once created
320    // If they ever become mutable, we'll need to rethink this.
321    if (!_private->cachedSubframeArchives) {
322        LegacyWebArchive* coreArchive = [_private coreArchive];
323        if (!coreArchive)
324            _private->cachedSubframeArchives = [[NSArray alloc] init];
325        else {
326            const Vector<RefPtr<Archive>>& subframeArchives(coreArchive->subframeArchives());
327            NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:subframeArchives.size()];
328            _private->cachedSubframeArchives = mutableArray;
329            for (unsigned i = 0; i < subframeArchives.size(); ++i) {
330                WebArchive *archive = [[WebArchive alloc] _initWithCoreLegacyWebArchive:(LegacyWebArchive *)subframeArchives[i].get()];
331                [mutableArray addObject:archive];
332                [archive release];
333            }
334        }
335    }
336
337    return [[_private->cachedSubframeArchives retain] autorelease];
338}
339
340- (NSData *)data
341{
342    WebCoreThreadViolationCheckRoundTwo();
343
344#if !LOG_DISABLED
345    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
346#endif
347
348    RetainPtr<CFDataRef> data = [_private coreArchive]->rawDataRepresentation();
349
350#if !LOG_DISABLED
351    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
352    CFAbsoluteTime duration = end - start;
353#endif
354    LOG(Timing, "Serializing web archive to raw CFPropertyList data took %f seconds", duration);
355
356    return [[(NSData *)data.get() retain] autorelease];
357}
358
359@end
360
361@implementation WebArchive (WebInternal)
362
363- (id)_initWithCoreLegacyWebArchive:(PassRefPtr<WebCore::LegacyWebArchive>)coreLegacyWebArchive
364{
365    WebCoreThreadViolationCheckRoundTwo();
366
367    self = [super init];
368    if (!self)
369        return nil;
370
371    _private = [[WebArchivePrivate alloc] initWithCoreArchive:coreLegacyWebArchive];
372    if (!_private) {
373        [self release];
374        return nil;
375    }
376
377    return self;
378}
379
380- (WebCore::LegacyWebArchive *)_coreLegacyWebArchive
381{
382    WebCoreThreadViolationCheckRoundTwo();
383
384    return [_private coreArchive];
385}
386
387@end
388