WebKit:
[WebKit-https.git] / WebKit / mac / WebView / WebArchive.mm
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
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 @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 + (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().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     coreArchive = _coreArchive.releaseRef();
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     WebCoreThreadViolationCheck();
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 #ifdef MAIL_THREAD_WORKAROUND
162     if (needMailThreadWorkaround()) {
163         // Maybe this could be done more cleanly with NSInvocation.
164         NSMutableDictionary *arguments = [[NSMutableDictionary alloc] init];
165         if (mainResource)
166             [arguments setObject:mainResource forKey:@"mainResource"];
167         if (subresources)
168             [arguments setObject:subresources forKey:@"subresources"];
169         if (subframeArchives)
170             [arguments setObject:subframeArchives forKey:@"subframeArchives"];
171         [self performSelectorOnMainThread:@selector(_initWithArguments:) withObject:arguments waitUntilDone:TRUE];
172         NSException *exception = [[[arguments objectForKey:@"exception"] retain] autorelease];
173         id result = [[[arguments objectForKey:@"result"] retain] autorelease];
174         [arguments release];
175         if (exception)
176             [exception raise];
177         return result;
178     }
179 #endif
180
181     WebCoreThreadViolationCheck();
182
183     self = [super init];
184     if (!self)
185         return nil;
186
187     _private = [[WebArchivePrivate alloc] init];
188
189     _private->cachedMainResource = [mainResource retain];
190     if (!_private->cachedMainResource) {
191         [self release];
192         return nil;
193     }
194     
195     if (!subresources || isArrayOfClass(subresources, [WebResource class]))
196         _private->cachedSubresources = [subresources retain];
197     else {
198         [self release];
199         return nil;
200     }
201
202     if (!subframeArchives || isArrayOfClass(subframeArchives, [WebArchive class]))
203         _private->cachedSubframeArchives = [subframeArchives retain];
204     else {
205         [self release];
206         return nil;
207     }
208     
209     RefPtr<ArchiveResource> coreMainResource = mainResource ? [mainResource _coreResource] : 0;
210
211     Vector<PassRefPtr<ArchiveResource> > coreResources;
212     NSEnumerator *enumerator = [subresources objectEnumerator];
213     WebResource *subresource;
214     while ((subresource = [enumerator nextObject]) != nil)
215         coreResources.append([subresource _coreResource]);
216
217     Vector<PassRefPtr<LegacyWebArchive> > coreArchives;
218     enumerator = [subframeArchives objectEnumerator];
219     WebArchive *subframeArchive;
220     while ((subframeArchive = [enumerator nextObject]) != nil)
221         coreArchives.append([subframeArchive->_private coreArchive]);
222
223     [_private setCoreArchive:LegacyWebArchive::create(coreMainResource.release(), coreResources, coreArchives)];
224     if (![_private coreArchive]) {
225         [self release];
226         return nil;
227     }
228
229     return self;
230 }
231
232 - (id)initWithData:(NSData *)data
233 {
234     WebCoreThreadViolationCheck();
235
236     self = [super init];
237     if (!self)
238         return nil;
239         
240 #if !LOG_DISABLED
241     CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
242 #endif
243
244     _private = [[WebArchivePrivate alloc] init];
245     [_private setCoreArchive:LegacyWebArchive::create(SharedBuffer::wrapNSData(data).get())];
246         
247 #if !LOG_DISABLED
248     CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
249     CFAbsoluteTime duration = end - start;
250 #endif
251     LOG(Timing, "Parsing web archive with [NSPropertyListSerialization propertyListFromData::::] took %f seconds", duration);
252     
253     return self;
254 }
255
256 - (id)initWithCoder:(NSCoder *)decoder
257 {    
258     WebResource *mainResource = nil;
259     NSArray *subresources = nil;
260     NSArray *subframeArchives = nil;
261     
262     @try {
263         id object = [decoder decodeObjectForKey:WebMainResourceKey];
264         if ([object isKindOfClass:[WebResource class]])
265             mainResource = [object retain];
266         object = [decoder decodeObjectForKey:WebSubresourcesKey];
267         if (isArrayOfClass(object, [WebResource class]))
268             subresources = [object retain];
269         object = [decoder decodeObjectForKey:WebSubframeArchivesKey];
270         if (isArrayOfClass(object, [WebArchive class]))
271             subframeArchives = [object retain];
272     } @catch(id) {
273         [self release];
274         return nil;
275     }
276
277     return [self initWithMainResource:mainResource subresources:subresources subframeArchives:subframeArchives];
278 }
279
280 - (void)encodeWithCoder:(NSCoder *)encoder
281 {
282     [encoder encodeObject:[self mainResource] forKey:WebMainResourceKey];
283     [encoder encodeObject:[self subresources] forKey:WebSubresourcesKey];
284     [encoder encodeObject:[self subframeArchives] forKey:WebSubframeArchivesKey];    
285 }
286
287 - (void)dealloc
288 {
289     [_private release];
290     [super dealloc];
291 }
292
293 - (id)copyWithZone:(NSZone *)zone
294 {
295     return [self retain];
296 }
297
298 - (WebResource *)mainResource
299 {
300 #ifdef MAIL_THREAD_WORKAROUND
301     if (needMailThreadWorkaround())
302         return [self _webkit_getPropertyOnMainThread:_cmd];
303 #endif
304
305     WebCoreThreadViolationCheck();
306
307     // Currently from WebKit API perspective, WebArchives are entirely immutable once created
308     // If they ever become mutable, we'll need to rethink this. 
309     if (!_private->cachedMainResource) {
310         LegacyWebArchive* coreArchive = [_private coreArchive];
311         if (coreArchive)
312             _private->cachedMainResource = [[WebResource alloc] _initWithCoreResource:coreArchive->mainResource()];
313     }
314     
315     return [[_private->cachedMainResource retain] autorelease];
316 }
317
318 - (NSArray *)subresources
319 {
320 #ifdef MAIL_THREAD_WORKAROUND
321     if (needMailThreadWorkaround())
322         return [self _webkit_getPropertyOnMainThread:_cmd];
323 #endif
324
325     WebCoreThreadViolationCheck();
326
327     // Currently from WebKit API perspective, WebArchives are entirely immutable once created
328     // If they ever become mutable, we'll need to rethink this.     
329     if (!_private->cachedSubresources) {
330         LegacyWebArchive* coreArchive = [_private coreArchive];
331         if (!coreArchive)
332             _private->cachedSubresources = [[NSArray alloc] init];
333         else {
334             const Vector<RefPtr<ArchiveResource> >& subresources(coreArchive->subresources());
335             NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:subresources.size()];
336             _private->cachedSubresources = mutableArray;
337             for (unsigned i = 0; i < subresources.size(); ++i) {
338                 WebResource *resource = [[WebResource alloc] _initWithCoreResource:subresources[i].get()];
339                 if (resource) {
340                     [mutableArray addObject:resource];
341                     [resource release];
342                 }
343             }
344         }
345     }
346     
347     return [[_private->cachedSubresources retain] autorelease];
348 }
349
350 - (NSArray *)subframeArchives
351 {
352 #ifdef MAIL_THREAD_WORKAROUND
353     if (needMailThreadWorkaround())
354         return [self _webkit_getPropertyOnMainThread:_cmd];
355 #endif
356
357     WebCoreThreadViolationCheck();
358
359     // Currently from WebKit API perspective, WebArchives are entirely immutable once created
360     // If they ever become mutable, we'll need to rethink this.  
361     if (!_private->cachedSubframeArchives) {
362         LegacyWebArchive* coreArchive = [_private coreArchive];
363         if (!coreArchive)
364             _private->cachedSubframeArchives = [[NSArray alloc] init];
365         else {
366             const Vector<RefPtr<Archive> >& subframeArchives(coreArchive->subframeArchives());
367             NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithCapacity:subframeArchives.size()];
368             _private->cachedSubframeArchives = mutableArray;
369             for (unsigned i = 0; i < subframeArchives.size(); ++i) {
370                 WebArchive *archive = [[WebArchive alloc] _initWithCoreLegacyWebArchive:(LegacyWebArchive *)subframeArchives[i].get()];
371                 [mutableArray addObject:archive];
372                 [archive release];
373             }
374         }
375     }
376     
377     return [[_private->cachedSubframeArchives retain] autorelease];
378 }
379
380 - (NSData *)data
381 {
382     WebCoreThreadViolationCheck();
383
384 #if !LOG_DISABLED
385     CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
386 #endif
387
388     RetainPtr<CFDataRef> data = [_private coreArchive]->rawDataRepresentation();
389     
390 #if !LOG_DISABLED
391     CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
392     CFAbsoluteTime duration = end - start;
393 #endif
394     LOG(Timing, "Serializing web archive to raw CFPropertyList data took %f seconds", duration);
395         
396     return [[(NSData *)data.get() retain] autorelease];
397 }
398
399 @end
400
401 @implementation WebArchive (WebInternal)
402
403 - (id)_initWithCoreLegacyWebArchive:(PassRefPtr<WebCore::LegacyWebArchive>)coreLegacyWebArchive
404 {
405     WebCoreThreadViolationCheck();
406
407     self = [super init];
408     if (!self)
409         return nil;
410     
411     _private = [[WebArchivePrivate alloc] initWithCoreArchive:coreLegacyWebArchive];
412     if (!_private) {
413         [self release];
414         return nil;
415     }
416
417     return self;
418 }
419
420 - (WebCore::LegacyWebArchive *)_coreLegacyWebArchive
421 {
422     WebCoreThreadViolationCheck();
423
424     return [_private coreArchive];
425 }
426
427 @end
428
429 #ifdef MAIL_THREAD_WORKAROUND
430
431 @implementation WebArchive (WebMailThreadWorkaround)
432
433 - (void)_initWithArguments:(NSMutableDictionary *)arguments
434 {
435     WebResource *mainResource = [arguments objectForKey:@"mainResource"];
436     NSArray *subresources = [arguments objectForKey:@"subresources"];
437     NSArray *subframeArchives = [arguments objectForKey:@"subframeArchives"];
438     @try {
439         id result = [self initWithMainResource:mainResource subresources:subresources subframeArchives:subframeArchives];
440         if (result)
441             [arguments setObject:result forKey:@"result"];
442     } @catch(NSException *exception) {
443         [arguments setObject:exception forKey:@"exception"];
444     }
445 }
446
447 @end
448
449 #endif