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 "WebResourceInternal.h" 30 31#import "WebFrameInternal.h" 32#import "WebKitLogging.h" 33#import "WebKitVersionChecks.h" 34#import "WebNSDictionaryExtras.h" 35#import "WebNSObjectExtras.h" 36#import "WebNSURLExtras.h" 37#import <JavaScriptCore/InitializeThreading.h> 38#import <JavaScriptCore/PassRefPtr.h> 39#import <WebCore/ArchiveResource.h> 40#import <WebCore/LegacyWebArchive.h> 41#import <WebCore/RuntimeApplicationChecks.h> 42#import <WebCore/TextEncoding.h> 43#import <WebCore/ThreadCheck.h> 44#import <WebCore/WebCoreObjCExtras.h> 45#import <WebCore/WebCoreURLResponse.h> 46#import <wtf/Threading.h> 47 48using namespace WebCore; 49 50static NSString * const WebResourceDataKey = @"WebResourceData"; 51static NSString * const WebResourceFrameNameKey = @"WebResourceFrameName"; 52static NSString * const WebResourceMIMETypeKey = @"WebResourceMIMEType"; 53static NSString * const WebResourceURLKey = @"WebResourceURL"; 54static NSString * const WebResourceTextEncodingNameKey = @"WebResourceTextEncodingName"; 55static NSString * const WebResourceResponseKey = @"WebResourceResponse"; 56 57@interface WebResourcePrivate : NSObject { 58@public 59 ArchiveResource* coreResource; 60} 61- (id)initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource; 62@end 63 64@implementation WebResourcePrivate 65 66+ (void)initialize 67{ 68 JSC::initializeThreading(); 69 WTF::initializeMainThreadToProcessMainThread(); 70#ifndef BUILDING_ON_TIGER 71 WebCoreObjCFinalizeOnMainThread(self); 72#endif 73} 74 75- (id)init 76{ 77 return [super init]; 78} 79 80- (id)initWithCoreResource:(PassRefPtr<ArchiveResource>)passedResource 81{ 82 self = [super init]; 83 if (!self) 84 return nil; 85 // Acquire the PassRefPtr<>'s ref as our own manual ref 86 coreResource = passedResource.releaseRef(); 87 return self; 88} 89 90- (void)dealloc 91{ 92 if (WebCoreObjCScheduleDeallocateOnMainThread([WebResourcePrivate class], self)) 93 return; 94 95 if (coreResource) 96 coreResource->deref(); 97 [super dealloc]; 98} 99 100- (void)finalize 101{ 102 if (coreResource) 103 coreResource->deref(); 104 [super finalize]; 105} 106 107@end 108 109@implementation WebResource 110 111- (id)init 112{ 113 self = [super init]; 114 if (!self) 115 return nil; 116 _private = [[WebResourcePrivate alloc] init]; 117 return self; 118} 119 120- (id)initWithData:(NSData *)data URL:(NSURL *)URL MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName frameName:(NSString *)frameName 121{ 122 return [self _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:nil copyData:YES]; 123} 124 125- (id)initWithCoder:(NSCoder *)decoder 126{ 127 WebCoreThreadViolationCheckRoundTwo(); 128 129 self = [super init]; 130 if (!self) 131 return nil; 132 133 NSData *data = nil; 134 NSURL *url = nil; 135 NSString *mimeType = nil, *textEncoding = nil, *frameName = nil; 136 NSURLResponse *response = nil; 137 138 @try { 139 id object = [decoder decodeObjectForKey:WebResourceDataKey]; 140 if ([object isKindOfClass:[NSData class]]) 141 data = object; 142 object = [decoder decodeObjectForKey:WebResourceURLKey]; 143 if ([object isKindOfClass:[NSURL class]]) 144 url = object; 145 object = [decoder decodeObjectForKey:WebResourceMIMETypeKey]; 146 if ([object isKindOfClass:[NSString class]]) 147 mimeType = object; 148 object = [decoder decodeObjectForKey:WebResourceTextEncodingNameKey]; 149 if ([object isKindOfClass:[NSString class]]) 150 textEncoding = object; 151 object = [decoder decodeObjectForKey:WebResourceFrameNameKey]; 152 if ([object isKindOfClass:[NSString class]]) 153 frameName = object; 154 object = [decoder decodeObjectForKey:WebResourceResponseKey]; 155 if ([object isKindOfClass:[NSURLResponse class]]) 156 response = object; 157 } @catch(id) { 158 [self release]; 159 return nil; 160 } 161 162 _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(data), url, mimeType, textEncoding, frameName, response)]; 163 164 return self; 165} 166 167- (void)encodeWithCoder:(NSCoder *)encoder 168{ 169 ArchiveResource *resource = _private->coreResource; 170 171 NSData *data = nil; 172 NSURL *url = nil; 173 NSString *mimeType = nil, *textEncoding = nil, *frameName = nil; 174 NSURLResponse *response = nil; 175 176 if (resource) { 177 if (resource->data()) 178 data = [resource->data()->createNSData() autorelease]; 179 url = resource->url(); 180 mimeType = resource->mimeType(); 181 textEncoding = resource->textEncoding(); 182 frameName = resource->frameName(); 183 response = resource->response().nsURLResponse(); 184 } 185 [encoder encodeObject:data forKey:WebResourceDataKey]; 186 [encoder encodeObject:url forKey:WebResourceURLKey]; 187 [encoder encodeObject:mimeType forKey:WebResourceMIMETypeKey]; 188 [encoder encodeObject:textEncoding forKey:WebResourceTextEncodingNameKey]; 189 [encoder encodeObject:frameName forKey:WebResourceFrameNameKey]; 190 [encoder encodeObject:response forKey:WebResourceResponseKey]; 191} 192 193- (void)dealloc 194{ 195 [_private release]; 196 [super dealloc]; 197} 198 199- (id)copyWithZone:(NSZone *)zone 200{ 201 return [self retain]; 202} 203 204- (NSData *)data 205{ 206#ifdef MAIL_THREAD_WORKAROUND 207 if (needMailThreadWorkaround()) 208 return [[self _webkit_invokeOnMainThread] data]; 209#endif 210 211 WebCoreThreadViolationCheckRoundTwo(); 212 213 if (!_private->coreResource) 214 return nil; 215 if (!_private->coreResource->data()) 216 return nil; 217 return [_private->coreResource->data()->createNSData() autorelease]; 218} 219 220- (NSURL *)URL 221{ 222#ifdef MAIL_THREAD_WORKAROUND 223 if (needMailThreadWorkaround()) 224 return [[self _webkit_invokeOnMainThread] URL]; 225#endif 226 227 WebCoreThreadViolationCheckRoundTwo(); 228 229 if (!_private->coreResource) 230 return nil; 231 NSURL *url = _private->coreResource->url(); 232 return url; 233} 234 235- (NSString *)MIMEType 236{ 237#ifdef MAIL_THREAD_WORKAROUND 238 if (needMailThreadWorkaround()) 239 return [[self _webkit_invokeOnMainThread] MIMEType]; 240#endif 241 242 WebCoreThreadViolationCheckRoundTwo(); 243 244 if (!_private->coreResource) 245 return nil; 246 NSString *mimeType = _private->coreResource->mimeType(); 247 return mimeType; 248} 249 250- (NSString *)textEncodingName 251{ 252#ifdef MAIL_THREAD_WORKAROUND 253 if (needMailThreadWorkaround()) 254 return [[self _webkit_invokeOnMainThread] textEncodingName]; 255#endif 256 257 WebCoreThreadViolationCheckRoundTwo(); 258 259 if (!_private->coreResource) 260 return nil; 261 NSString *textEncodingName = _private->coreResource->textEncoding(); 262 return textEncodingName; 263} 264 265- (NSString *)frameName 266{ 267#ifdef MAIL_THREAD_WORKAROUND 268 if (needMailThreadWorkaround()) 269 return [[self _webkit_invokeOnMainThread] frameName]; 270#endif 271 272 WebCoreThreadViolationCheckRoundTwo(); 273 274 if (!_private->coreResource) 275 return nil; 276 NSString *frameName = _private->coreResource->frameName(); 277 return frameName; 278} 279 280- (NSString *)description 281{ 282 return [NSString stringWithFormat:@"<%@ %@>", [self className], [self URL]]; 283} 284 285@end 286 287@implementation WebResource (WebResourceInternal) 288 289- (id)_initWithCoreResource:(PassRefPtr<ArchiveResource>)coreResource 290{ 291 self = [super init]; 292 if (!self) 293 return nil; 294 295 ASSERT(coreResource); 296 297 // WebResources should not be init'ed with nil data, and doing so breaks certain uses of NSHTMLReader 298 // See <rdar://problem/5820157> for more info 299 if (!coreResource->data()) { 300 [self release]; 301 return nil; 302 } 303 304 _private = [[WebResourcePrivate alloc] initWithCoreResource:coreResource]; 305 306 return self; 307} 308 309- (WebCore::ArchiveResource *)_coreResource 310{ 311 return _private->coreResource; 312} 313 314@end 315 316@implementation WebResource (WebResourcePrivate) 317 318// SPI for Mail (5066325) 319// FIXME: This "ignoreWhenUnarchiving" concept is an ugly one - can we find a cleaner solution for those who need this SPI? 320- (void)_ignoreWhenUnarchiving 321{ 322#ifdef MAIL_THREAD_WORKAROUND 323 if (needMailThreadWorkaround()) { 324 [[self _webkit_invokeOnMainThread] _ignoreWhenUnarchiving]; 325 return; 326 } 327#endif 328 329 WebCoreThreadViolationCheckRoundTwo(); 330 331 if (!_private->coreResource) 332 return; 333 _private->coreResource->ignoreWhenUnarchiving(); 334} 335 336- (id)_initWithData:(NSData *)data 337 URL:(NSURL *)URL 338 MIMEType:(NSString *)MIMEType 339 textEncodingName:(NSString *)textEncodingName 340 frameName:(NSString *)frameName 341 response:(NSURLResponse *)response 342 copyData:(BOOL)copyData 343{ 344#ifdef MAIL_THREAD_WORKAROUND 345 if (needMailThreadWorkaround()) 346 return [[self _webkit_invokeOnMainThread] _initWithData:data URL:URL MIMEType:MIMEType textEncodingName:textEncodingName frameName:frameName response:response copyData:copyData]; 347#endif 348 349 WebCoreThreadViolationCheckRoundTwo(); 350 351 self = [super init]; 352 if (!self) 353 return nil; 354 355 if (!data || !URL || !MIMEType) { 356 [self release]; 357 return nil; 358 } 359 360 _private = [[WebResourcePrivate alloc] initWithCoreResource:ArchiveResource::create(SharedBuffer::wrapNSData(copyData ? [[data copy] autorelease] : data), URL, MIMEType, textEncodingName, frameName, response)]; 361 362 return self; 363} 364 365- (id)_initWithData:(NSData *)data URL:(NSURL *)URL response:(NSURLResponse *)response 366{ 367 // Pass NO for copyData since the data doesn't need to be copied since we know that callers will no longer modify it. 368 // Copying it will also cause a performance regression. 369 return [self _initWithData:data 370 URL:URL 371 MIMEType:[response MIMEType] 372 textEncodingName:[response textEncodingName] 373 frameName:nil 374 response:response 375 copyData:NO]; 376} 377 378- (NSString *)_suggestedFilename 379{ 380#ifdef MAIL_THREAD_WORKAROUND 381 if (needMailThreadWorkaround()) 382 return [[self _webkit_invokeOnMainThread] _suggestedFilename]; 383#endif 384 385 WebCoreThreadViolationCheckRoundTwo(); 386 387 if (!_private->coreResource) 388 return nil; 389 NSString *suggestedFilename = _private->coreResource->response().suggestedFilename(); 390 return suggestedFilename; 391} 392 393- (NSFileWrapper *)_fileWrapperRepresentation 394{ 395 NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[self data]] autorelease]; 396 NSString *filename = [self _suggestedFilename]; 397 if (!filename || ![filename length]) 398 filename = [[self URL] _webkit_suggestedFilenameWithMIMEType:[self MIMEType]]; 399 [wrapper setPreferredFilename:filename]; 400 return wrapper; 401} 402 403- (NSURLResponse *)_response 404{ 405#ifdef MAIL_THREAD_WORKAROUND 406 if (needMailThreadWorkaround()) 407 return [[self _webkit_invokeOnMainThread] _response]; 408#endif 409 410 WebCoreThreadViolationCheckRoundTwo(); 411 412 NSURLResponse *response = nil; 413 if (_private->coreResource) 414 response = _private->coreResource->response().nsURLResponse(); 415 return response ? response : [[[NSURLResponse alloc] init] autorelease]; 416} 417 418- (NSString *)_stringValue 419{ 420#ifdef MAIL_THREAD_WORKAROUND 421 if (needMailThreadWorkaround()) 422 return [[self _webkit_invokeOnMainThread] _stringValue]; 423#endif 424 425 WebCoreThreadViolationCheckRoundTwo(); 426 427 WebCore::TextEncoding encoding; 428 if (_private->coreResource) 429 encoding = _private->coreResource->textEncoding(); 430 if (!encoding.isValid()) 431 encoding = WindowsLatin1Encoding(); 432 433 SharedBuffer* coreData = _private->coreResource ? _private->coreResource->data() : 0; 434 return encoding.decode(reinterpret_cast<const char*>(coreData ? coreData->data() : 0), coreData ? coreData->size() : 0); 435} 436 437@end 438 439#ifdef MAIL_THREAD_WORKAROUND 440 441static const double newMailBundleVersion = 1050.0; 442 443@implementation WebResource (WebMailThreadWorkaround) 444 445+ (BOOL)_needMailThreadWorkaroundIfCalledOffMainThread 446{ 447 static BOOL isOldMail = applicationIsAppleMail() && [[[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] doubleValue] < newMailBundleVersion; 448 return isOldMail; 449} 450 451@end 452 453#endif 454