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 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 133 static 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