/* * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "WebArchive.h" #import "WebArchiveInternal.h" #import "WebKitLogging.h" #import "WebNSObjectExtras.h" #import "WebResourceInternal.h" #import #import #import #import #import #import #import #import #import using namespace WebCore; NSString *WebArchivePboardType = @"Apple Web Archive pasteboard type"; static NSString * const WebMainResourceKey = @"WebMainResource"; static NSString * const WebSubresourcesKey = @"WebSubresources"; static NSString * const WebSubframeArchivesKey = @"WebSubframeArchives"; @interface WebArchivePrivate : NSObject { @public RetainPtr cachedMainResource; RetainPtr cachedSubresources; RetainPtr cachedSubframeArchives; @private RefPtr coreArchive; } - (instancetype)initWithCoreArchive:(RefPtr&&)coreArchive; - (LegacyWebArchive*)coreArchive; - (void)setCoreArchive:(Ref&&)newCoreArchive; @end @implementation WebArchivePrivate + (void)initialize { #if !PLATFORM(IOS_FAMILY) JSC::initialize(); WTF::initializeMainThread(); WebCore::populateJITOperations(); #endif } - (instancetype)init { self = [super init]; if (!self) return nil; coreArchive = LegacyWebArchive::create(); return self; } - (instancetype)initWithCoreArchive:(RefPtr&&)_coreArchive { self = [super init]; if (!self|| !_coreArchive) { [self release]; return nil; } coreArchive = WTFMove(_coreArchive); return self; } - (LegacyWebArchive*)coreArchive { return coreArchive.get(); } - (void)setCoreArchive:(Ref&&)newCoreArchive { ASSERT(coreArchive); coreArchive = WTFMove(newCoreArchive); } - (void)dealloc { if (WebCoreObjCScheduleDeallocateOnMainThread([WebArchivePrivate class], self)) return; [super dealloc]; } @end @implementation WebArchive - (instancetype)init { WebCoreThreadViolationCheckRoundTwo(); self = [super init]; if (!self) return nil; _private = [[WebArchivePrivate alloc] init]; return self; } static BOOL isArrayOfClass(id object, Class elementClass) { if (![object isKindOfClass:[NSArray class]]) return NO; NSArray *array = (NSArray *)object; NSUInteger count = [array count]; for (NSUInteger i = 0; i < count; ++i) if (![[array objectAtIndex:i] isKindOfClass:elementClass]) return NO; return YES; } - (instancetype)initWithMainResource:(WebResource *)mainResource subresources:(NSArray *)subresources subframeArchives:(NSArray *)subframeArchives { WebCoreThreadViolationCheckRoundTwo(); self = [super init]; if (!self) return nil; _private = [[WebArchivePrivate alloc] init]; _private->cachedMainResource = mainResource; if (!_private->cachedMainResource) { [self release]; return nil; } if (!subresources || isArrayOfClass(subresources, [WebResource class])) _private->cachedSubresources = subresources; else { [self release]; return nil; } if (!subframeArchives || isArrayOfClass(subframeArchives, [WebArchive class])) _private->cachedSubframeArchives = subframeArchives; else { [self release]; return nil; } Vector> coreResources; for (WebResource *subresource in subresources) coreResources.append([subresource _coreResource].get()); Vector> coreArchives; for (WebArchive *subframeArchive in subframeArchives) coreArchives.append(*[subframeArchive->_private coreArchive]); [_private setCoreArchive:LegacyWebArchive::create([mainResource _coreResource].get(), WTFMove(coreResources), WTFMove(coreArchives))]; return self; } - (instancetype)initWithData:(NSData *)data { WebCoreThreadViolationCheckRoundTwo(); self = [super init]; if (!self) return nil; #if !LOG_DISABLED CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); #endif _private = [[WebArchivePrivate alloc] init]; auto coreArchive = LegacyWebArchive::create(SharedBuffer::create(data)); if (!coreArchive) { [self release]; return nil; } [_private setCoreArchive:coreArchive.releaseNonNull()]; #if !LOG_DISABLED CFAbsoluteTime end = CFAbsoluteTimeGetCurrent(); CFAbsoluteTime duration = end - start; #endif LOG(Timing, "Parsing web archive with [NSPropertyListSerialization propertyListFromData::::] took %f seconds", duration); return self; } - (instancetype)initWithCoder:(NSCoder *)decoder { WebResource *mainResource = nil; NSArray *subresources = nil; NSArray *subframeArchives = nil; @try { id object = [decoder decodeObjectForKey:WebMainResourceKey]; if ([object isKindOfClass:[WebResource class]]) mainResource = object; object = [decoder decodeObjectForKey:WebSubresourcesKey]; if (isArrayOfClass(object, [WebResource class])) subresources = object; object = [decoder decodeObjectForKey:WebSubframeArchivesKey]; if (isArrayOfClass(object, [WebArchive class])) subframeArchives = object; } @catch(id) { [self release]; return nil; } return [self initWithMainResource:mainResource subresources:subresources subframeArchives:subframeArchives]; } - (void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject:[self mainResource] forKey:WebMainResourceKey]; [encoder encodeObject:[self subresources] forKey:WebSubresourcesKey]; [encoder encodeObject:[self subframeArchives] forKey:WebSubframeArchivesKey]; } - (void)dealloc { [_private release]; [super dealloc]; } - (id)copyWithZone:(NSZone *)zone { return [self retain]; } - (WebResource *)mainResource { WebCoreThreadViolationCheckRoundTwo(); // Currently from WebKit API perspective, WebArchives are entirely immutable once created // If they ever become mutable, we'll need to rethink this. if (!_private->cachedMainResource) { if (auto* coreArchive = [_private coreArchive]) { if (auto* mainResource = coreArchive->mainResource()) _private->cachedMainResource = adoptNS([[WebResource alloc] _initWithCoreResource:*mainResource]); } } auto cachedMainResourceCopy = _private->cachedMainResource; return cachedMainResourceCopy.autorelease(); } - (NSArray *)subresources { WebCoreThreadViolationCheckRoundTwo(); // Currently from WebKit API perspective, WebArchives are entirely immutable once created // If they ever become mutable, we'll need to rethink this. if (!_private->cachedSubresources) { auto coreArchive = [_private coreArchive]; if (!coreArchive) _private->cachedSubresources = adoptNS([[NSArray alloc] init]); else { _private->cachedSubresources = createNSArray(coreArchive->subresources(), [] (auto& subresource) { return adoptNS([[WebResource alloc] _initWithCoreResource:subresource.get()]); }); } } // Maintain the WebKit 3 behavior of this API, which is documented and // relied upon by some clients, of returning nil if there are no subresources. if (![_private->cachedSubresources count]) return nil; auto cachedSubresourcesCopy = _private->cachedSubresources; return cachedSubresourcesCopy.autorelease(); } - (NSArray *)subframeArchives { WebCoreThreadViolationCheckRoundTwo(); // Currently from WebKit API perspective, WebArchives are entirely immutable once created // If they ever become mutable, we'll need to rethink this. if (!_private->cachedSubframeArchives) { auto* coreArchive = [_private coreArchive]; if (!coreArchive) _private->cachedSubframeArchives = adoptNS([[NSArray alloc] init]); else { _private->cachedSubframeArchives = createNSArray(coreArchive->subframeArchives(), [] (auto& archive) { return adoptNS([[WebArchive alloc] _initWithCoreLegacyWebArchive:static_cast(archive.ptr())]); }); } } auto cachedSubframeArchivesCopy = _private->cachedSubframeArchives; return cachedSubframeArchivesCopy.autorelease(); } - (NSData *)data { WebCoreThreadViolationCheckRoundTwo(); #if !LOG_DISABLED CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); #endif RetainPtr data = [_private coreArchive]->rawDataRepresentation(); #if !LOG_DISABLED CFAbsoluteTime end = CFAbsoluteTimeGetCurrent(); CFAbsoluteTime duration = end - start; #endif LOG(Timing, "Serializing web archive to raw CFPropertyList data took %f seconds", duration); return retainPtr((NSData *)data.get()).autorelease(); } @end @implementation WebArchive (WebInternal) - (id)_initWithCoreLegacyWebArchive:(RefPtr&&)coreLegacyWebArchive { WebCoreThreadViolationCheckRoundTwo(); self = [super init]; if (!self) return nil; _private = [[WebArchivePrivate alloc] initWithCoreArchive:WTFMove(coreLegacyWebArchive)]; if (!_private) { [self release]; return nil; } return self; } - (WebCore::LegacyWebArchive *)_coreLegacyWebArchive { WebCoreThreadViolationCheckRoundTwo(); return [_private coreArchive]; } @end