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