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