/* * 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 Computer, 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 "WebResourceInternal.h" #import "WebFrameInternal.h" #import "WebKitLogging.h" #import "WebKitVersionChecks.h" #import "WebNSDictionaryExtras.h" #import "WebNSObjectExtras.h" #import "WebNSURLExtras.h" #import <JavaScriptCore/InitializeThreading.h> #import <JavaScriptCore/PassRefPtr.h> #import <WebCore/ArchiveResource.h> #import <WebCore/LegacyWebArchive.h> #import <WebCore/RuntimeApplicationChecks.h> #import <WebCore/TextEncoding.h> #import <WebCore/ThreadCheck.h> #import <WebCore/WebCoreObjCExtras.h> #import <WebCore/WebCoreURLResponse.h> #import <wtf/Threading.h> using namespace WebCore; static NSString * const WebResourceDataKey = @"WebResourceData"; static NSString * const WebResourceFrameNameKey = @"WebResourceFrameName"; static NSString * const WebResourceMIMETypeKey = @"WebResourceMIMEType"; static NSString * const WebResourceURLKey = @"WebResourceURL"; static NSString * const WebResourceTextEncodingNameKey = @"WebResourceTextEncodingName"; static NSString * const WebResourceResponseKey = @"WebResourceResponse"; @interface WebResourcePrivate : NSObject { @public ArchiveResource* coreResource; } - (id)initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource; @end @implementation WebResourcePrivate + (void)initialize { JSC::initializeThreading(); WTF::initializeMainThreadToProcessMainThread(); #ifndef BUILDING_ON_TIGER WebCoreObjCFinalizeOnMainThread(self); #endif } - (id)init { return [super init]; } - (id)initWithCoreResource:(PassRefPtr<ArchiveResource>)passedResource { self = [super init]; if (!self) return nil; // Acquire the PassRefPtr<>'s ref as our own manual ref coreResource = passedResource.releaseRef(); return self; } - (void)dealloc { if (WebCoreObjCScheduleDeallocateOnMainThread([WebResourcePrivate class], self)) return; if (coreResource) coreResource->deref(); [super dealloc]; } - (void)finalize { if (coreResource) coreResource->deref(); [super finalize]; } @end @implementation WebResource - (id)init { self = [super init]; if (!self) return nil; _private = [[WebResourcePrivate alloc] init]; return self; } - (id)initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName { return [self _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:nil copyData:YES]; } - (id)initWithCoder:(NSCoder *)decoder { WebCoreThreadViolationCheckRoundTwo(); self = [super init]; if (!self) return nil; NSData *data = nil; NSURL *url = nil; NSString *mimeType = nil, *textEncoding = nil, *frameName = nil; NSURLResponse *response = nil; @try { id object = [decoder decodeObjectForKey:WebResourceDataKey]; if ([object isKindOfClass:[NSData class]]) data = object; object = [decoder decodeObjectForKey:WebResourceURLKey]; if ([object isKindOfClass:[NSURL class]]) url = object; object = [decoder decodeObjectForKey:WebResourceMIMETypeKey]; if ([object isKindOfClass:[NSString class]]) mimeType = object; object = [decoder decodeObjectForKey:WebResourceTextEncodingNameKey]; if ([object isKindOfClass:[NSString class]]) textEncoding = object; object = [decoder decodeObjectForKey:WebResourceFrameNameKey]; if ([object isKindOfClass:[NSString class]]) frameName = object; object = [decoder decodeObjectForKey:WebResourceResponseKey]; if ([object isKindOfClass:[NSURLResponse class]]) response = object; } @catch(id) { [self release]; return nil; } _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(data), url, mimeType, textEncoding, frameName, response)]; return self; } - (void)encodeWithCoder:(NSCoder *)encoder { ArchiveResource *resource = _private->coreResource; NSData *data = nil; NSURL *url = nil; NSString *mimeType = nil, *textEncoding = nil, *frameName = nil; NSURLResponse *response = nil; if (resource) { if (resource->data()) data = [resource->data()->createNSData() autorelease]; url = resource->url(); mimeType = resource->mimeType(); textEncoding = resource->textEncoding(); frameName = resource->frameName(); response = resource->response().nsURLResponse(); } [encoder encodeObject:data forKey:WebResourceDataKey]; [encoder encodeObject:url forKey:WebResourceURLKey]; [encoder encodeObject:mimeType forKey:WebResourceMIMETypeKey]; [encoder encodeObject:textEncoding forKey:WebResourceTextEncodingNameKey]; [encoder encodeObject:frameName forKey:WebResourceFrameNameKey]; [encoder encodeObject:response forKey:WebResourceResponseKey]; } - (void)dealloc { [_private release]; [super dealloc]; } - (id)copyWithZone:(NSZone *)zone { return [self retain]; } - (NSData *)data { #ifdef MAIL_THREAD_WORKAROUND if (needMailThreadWorkaround()) return [[self _webkit_invokeOnMainThread] data]; #endif WebCoreThreadViolationCheckRoundTwo(); if (!_private->coreResource) return nil; if (!_private->coreResource->data()) return nil; return [_private->coreResource->data()->createNSData() autorelease]; } - (NSURL *)URL { #ifdef MAIL_THREAD_WORKAROUND if (needMailThreadWorkaround()) return [[self _webkit_invokeOnMainThread] URL]; #endif WebCoreThreadViolationCheckRoundTwo(); if (!_private->coreResource) return nil; NSURL *url = _private->coreResource->url(); return url; } - (NSString *)MIMEType { #ifdef MAIL_THREAD_WORKAROUND if (needMailThreadWorkaround()) return [[self _webkit_invokeOnMainThread] MIMEType]; #endif WebCoreThreadViolationCheckRoundTwo(); if (!_private->coreResource) return nil; NSString *mimeType = _private->coreResource->mimeType(); return mimeType; } - (NSString *)textEncodingName { #ifdef MAIL_THREAD_WORKAROUND if (needMailThreadWorkaround()) return [[self _webkit_invokeOnMainThread] textEncodingName]; #endif WebCoreThreadViolationCheckRoundTwo(); if (!_private->coreResource) return nil; NSString *textEncodingName = _private->coreResource->textEncoding(); return textEncodingName; } - (NSString *)frameName { #ifdef MAIL_THREAD_WORKAROUND if (needMailThreadWorkaround()) return [[self _webkit_invokeOnMainThread] frameName]; #endif WebCoreThreadViolationCheckRoundTwo(); if (!_private->coreResource) return nil; NSString *frameName = _private->coreResource->frameName(); return frameName; } - (NSString *)description { return [NSString stringWithFormat:@"<%@ %@>", [self className], [self URL]]; } @end @implementation WebResource (WebResourceInternal) - (id)_initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource { self = [super init]; if (!self) return nil; ASSERT(coreResource); // WebResources should not be init'ed with nil data, and doing so breaks certain uses of NSHTMLReader // See <rdar://problem/5820157> for more info if (!coreResource->data()) { [self release]; return nil; } _private = [[WebResourcePrivate alloc] initWithCoreResource:coreResource]; return self; } - (WebCore::ArchiveResource *)_coreResource { return _private->coreResource; } @end @implementation WebResource (WebResourcePrivate) // SPI for Mail (5066325) // FIXME: This "ignoreWhenUnarchiving" concept is an ugly one - can we find a cleaner solution for those who need this SPI? - (void)_ignoreWhenUnarchiving { #ifdef MAIL_THREAD_WORKAROUND if (needMailThreadWorkaround()) { [[self _webkit_invokeOnMainThread] _ignoreWhenUnarchiving]; return; } #endif WebCoreThreadViolationCheckRoundTwo(); if (!_private->coreResource) return; _private->coreResource->ignoreWhenUnarchiving(); } - (id)_initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName response:(NSURLResponse *)response copyData:(BOOL)copyData { #ifdef MAIL_THREAD_WORKAROUND if (needMailThreadWorkaround()) return [[self _webkit_invokeOnMainThread] _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:response copyData:copyData]; #endif WebCoreThreadViolationCheckRoundTwo(); self = [super init]; if (!self) return nil; if (!data || !URL || !MIMEType) { [self release]; return nil; } _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(copyData ? [[data copy] autorelease] : data), URL, MIMEType, textEncodingName, frameName, response)]; return self; } - (id)_initWithData:(NSData *)data URL:(NSURL *)URL response:(NSURLResponse *)response { // Pass NO for copyData since the data doesn't need to be copied since we know that callers will no longer modify it. // Copying it will also cause a performance regression. return [self _initWithData:data URL:URL MIMEType:[response MIMEType] textEncodingName:[response textEncodingName] frameName:nil response:response copyData:NO]; } - (NSString *)_suggestedFilename { #ifdef MAIL_THREAD_WORKAROUND if (needMailThreadWorkaround()) return [[self _webkit_invokeOnMainThread] _suggestedFilename]; #endif WebCoreThreadViolationCheckRoundTwo(); if (!_private->coreResource) return nil; NSString *suggestedFilename = _private->coreResource->response().suggestedFilename(); return suggestedFilename; } - (NSFileWrapper *)_fileWrapperRepresentation { NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[self data]] autorelease]; NSString *filename = [self _suggestedFilename]; if (!filename || ![filename length]) filename = [[self URL] _webkit_suggestedFilenameWithMIMEType:[self MIMEType]]; [wrapper setPreferredFilename:filename]; return wrapper; } - (NSURLResponse *)_response { #ifdef MAIL_THREAD_WORKAROUND if (needMailThreadWorkaround()) return [[self _webkit_invokeOnMainThread] _response]; #endif WebCoreThreadViolationCheckRoundTwo(); NSURLResponse *response = nil; if (_private->coreResource) response = _private->coreResource->response().nsURLResponse(); return response ? response : [[[NSURLResponse alloc] init] autorelease]; } - (NSString *)_stringValue { #ifdef MAIL_THREAD_WORKAROUND if (needMailThreadWorkaround()) return [[self _webkit_invokeOnMainThread] _stringValue]; #endif WebCoreThreadViolationCheckRoundTwo(); WebCore::TextEncoding encoding; if (_private->coreResource) encoding = _private->coreResource->textEncoding(); if (!encoding.isValid()) encoding = WindowsLatin1Encoding(); SharedBuffer* coreData = _private->coreResource ? _private->coreResource->data() : 0; return encoding.decode(reinterpret_cast<const char*>(coreData ? coreData->data() : 0), coreData ? coreData->size() : 0); } @end #ifdef MAIL_THREAD_WORKAROUND static const double newMailBundleVersion = 1050.0; @implementation WebResource (WebMailThreadWorkaround) + (BOOL)_needMailThreadWorkaroundIfCalledOffMainThread { static BOOL isOldMail = applicationIsAppleMail() && [[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] doubleValue] < newMailBundleVersion; return isOldMail; } @end #endif