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