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