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 43 using namespace WebCore; 44 45 NSString *WebArchivePboardType = @"Apple Web Archive pasteboard type"; 46 47 static NSString * const WebMainResourceKey = @"WebMainResource"; 48 static NSString * const WebSubresourcesKey = @"WebSubresources"; 49 static 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 135 static 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