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