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