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 "WebDataSource.h" 30 31#import "WebArchive.h" 32#import "WebArchiveInternal.h" 33#import "WebDataSourceInternal.h" 34#import "WebDocument.h" 35#import "WebDocumentLoaderMac.h" 36#import "WebFrameInternal.h" 37#import "WebFrameLoadDelegate.h" 38#import "WebFrameLoaderClient.h" 39#import "WebHTMLRepresentation.h" 40#import "WebKitErrorsPrivate.h" 41#import "WebKitLogging.h" 42#import "WebKitStatisticsPrivate.h" 43#import "WebKitNSStringExtras.h" 44#import "WebNSURLExtras.h" 45#import "WebNSURLRequestExtras.h" 46#import "WebPDFRepresentation.h" 47#import "WebResourceInternal.h" 48#import "WebResourceLoadDelegate.h" 49#import "WebViewInternal.h" 50#import <WebCore/ApplicationCacheStorage.h> 51#import <WebCore/FrameLoader.h> 52#import <WebCore/KURL.h> 53#import <WebCore/LegacyWebArchive.h> 54#import <WebCore/MIMETypeRegistry.h> 55#import <WebCore/ResourceRequest.h> 56#import <WebCore/SharedBuffer.h> 57#import <WebCore/WebCoreObjCExtras.h> 58#import <WebCore/WebCoreURLResponse.h> 59#import <WebKit/DOMHTML.h> 60#import <WebKit/DOMPrivate.h> 61#import <runtime/InitializeThreading.h> 62#import <wtf/Assertions.h> 63 64using namespace WebCore; 65 66@interface WebDataSourcePrivate : NSObject { 67@public 68 WebDocumentLoaderMac* loader; 69 70 id <WebDocumentRepresentation> representation; 71 72 BOOL representationFinishedLoading; 73} 74@end 75 76@implementation WebDataSourcePrivate 77 78+ (void)initialize 79{ 80 JSC::initializeThreading(); 81#ifndef BUILDING_ON_TIGER 82 WebCoreObjCFinalizeOnMainThread(self); 83#endif 84} 85 86- (void)dealloc 87{ 88 if (WebCoreObjCScheduleDeallocateOnMainThread([WebDataSourcePrivate class], self)) 89 return; 90 91 ASSERT(!loader->isLoading()); 92 loader->detachDataSource(); 93 loader->deref(); 94 95 [representation release]; 96 97 [super dealloc]; 98} 99 100- (void)finalize 101{ 102 ASSERT_MAIN_THREAD(); 103 104 ASSERT(!loader->isLoading()); 105 loader->detachDataSource(); 106 loader->deref(); 107 108 [super finalize]; 109} 110 111@end 112 113@interface WebDataSource (WebFileInternal) 114@end 115 116@implementation WebDataSource (WebFileInternal) 117 118- (void)_setRepresentation:(id<WebDocumentRepresentation>)representation 119{ 120 [_private->representation release]; 121 _private->representation = [representation retain]; 122 _private->representationFinishedLoading = NO; 123} 124 125static inline void addTypesFromClass(NSMutableDictionary *allTypes, Class objCClass, NSArray *supportTypes) 126{ 127 NSEnumerator *enumerator = [supportTypes objectEnumerator]; 128 ASSERT(enumerator != nil); 129 NSString *mime = nil; 130 while ((mime = [enumerator nextObject]) != nil) { 131 // Don't clobber previously-registered classes. 132 if ([allTypes objectForKey:mime] == nil) 133 [allTypes setObject:objCClass forKey:mime]; 134 } 135} 136 137+ (Class)_representationClassForMIMEType:(NSString *)MIMEType 138{ 139 Class repClass; 140 return [WebView _viewClass:nil andRepresentationClass:&repClass forMIMEType:MIMEType] ? repClass : nil; 141} 142@end 143 144@implementation WebDataSource (WebPrivate) 145 146- (NSError *)_mainDocumentError 147{ 148 return _private->loader->mainDocumentError(); 149} 150 151- (void)_addSubframeArchives:(NSArray *)subframeArchives 152{ 153 // FIXME: This SPI is poor, poor design. Can we come up with another solution for those who need it? 154 DocumentLoader* loader = [self _documentLoader]; 155 ASSERT(loader); 156 157 NSEnumerator *enumerator = [subframeArchives objectEnumerator]; 158 WebArchive *archive; 159 while ((archive = [enumerator nextObject]) != nil) 160 loader->addAllArchiveResources([archive _coreLegacyWebArchive]); 161} 162 163- (NSFileWrapper *)_fileWrapperForURL:(NSURL *)URL 164{ 165 if ([URL isFileURL]) { 166 NSString *path = [[URL path] stringByResolvingSymlinksInPath]; 167 return [[[NSFileWrapper alloc] initWithPath:path] autorelease]; 168 } 169 170 WebResource *resource = [self subresourceForURL:URL]; 171 if (resource) 172 return [resource _fileWrapperRepresentation]; 173 174 NSCachedURLResponse *cachedResponse = [[self _webView] _cachedResponseForURL:URL]; 175 if (cachedResponse) { 176 NSFileWrapper *wrapper = [[[NSFileWrapper alloc] initRegularFileWithContents:[cachedResponse data]] autorelease]; 177 [wrapper setPreferredFilename:[[cachedResponse response] suggestedFilename]]; 178 return wrapper; 179 } 180 181 return nil; 182} 183 184- (NSString *)_responseMIMEType 185{ 186 return [[self response] _webcore_MIMEType]; 187} 188 189- (BOOL)_transferApplicationCache:(NSString*)destinationBundleIdentifier 190{ 191 DocumentLoader* loader = [self _documentLoader]; 192 193 if (!loader) 194 return NO; 195 196 ApplicationCache* cache = loader->applicationCache(); 197 if (!cache) 198 return YES; 199 200 NSString *cacheDir = [NSString _webkit_localCacheDirectoryWithBundleIdentifier:destinationBundleIdentifier]; 201 202 return ApplicationCacheStorage::storeCopyOfCache(cacheDir, cache); 203} 204 205@end 206 207@implementation WebDataSource (WebInternal) 208 209- (void)_finishedLoading 210{ 211 _private->representationFinishedLoading = YES; 212 [[self representation] finishedLoadingWithDataSource:self]; 213} 214 215- (void)_receivedData:(NSData *)data 216{ 217 // protect self temporarily, as the bridge receivedData call could remove our last ref 218 RetainPtr<WebDataSource*> protect(self); 219 220 [[self representation] receivedData:data withDataSource:self]; 221 [[[[self webFrame] frameView] documentView] dataSourceUpdated:self]; 222} 223 224- (void)_setMainDocumentError:(NSError *)error 225{ 226 if (!_private->representationFinishedLoading) { 227 _private->representationFinishedLoading = YES; 228 [[self representation] receivedError:error withDataSource:self]; 229 } 230} 231 232- (void)_revertToProvisionalState 233{ 234 [self _setRepresentation:nil]; 235} 236 237+ (NSMutableDictionary *)_repTypesAllowImageTypeOmission:(BOOL)allowImageTypeOmission 238{ 239 static NSMutableDictionary *repTypes = nil; 240 static BOOL addedImageTypes = NO; 241 242 if (!repTypes) { 243 repTypes = [[NSMutableDictionary alloc] init]; 244 addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedNonImageMIMETypes]); 245 246 // Since this is a "secret default" we don't both registering it. 247 BOOL omitPDFSupport = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitOmitPDFSupport"]; 248 if (!omitPDFSupport) 249 addTypesFromClass(repTypes, [WebPDFRepresentation class], [WebPDFRepresentation supportedMIMETypes]); 250 } 251 252 if (!addedImageTypes && !allowImageTypeOmission) { 253 addTypesFromClass(repTypes, [WebHTMLRepresentation class], [WebHTMLRepresentation supportedImageMIMETypes]); 254 addedImageTypes = YES; 255 } 256 257 return repTypes; 258} 259 260- (void)_replaceSelectionWithArchive:(WebArchive *)archive selectReplacement:(BOOL)selectReplacement 261{ 262 DOMDocumentFragment *fragment = [self _documentFragmentWithArchive:archive]; 263 if (fragment) 264 [[self webFrame] _replaceSelectionWithFragment:fragment selectReplacement:selectReplacement smartReplace:NO matchStyle:NO]; 265} 266 267// FIXME: There are few reasons why this method and many of its related methods can't be pushed entirely into WebCore in the future. 268- (DOMDocumentFragment *)_documentFragmentWithArchive:(WebArchive *)archive 269{ 270 ASSERT(archive); 271 WebResource *mainResource = [archive mainResource]; 272 if (mainResource) { 273 NSString *MIMEType = [mainResource MIMEType]; 274 if ([WebView canShowMIMETypeAsHTML:MIMEType]) { 275 NSString *markupString = [[NSString alloc] initWithData:[mainResource data] encoding:NSUTF8StringEncoding]; 276 // FIXME: seems poor form to do this as a side effect of getting a document fragment 277 if (DocumentLoader* loader = [self _documentLoader]) 278 loader->addAllArchiveResources([archive _coreLegacyWebArchive]); 279 280 DOMDocumentFragment *fragment = [[self webFrame] _documentFragmentWithMarkupString:markupString baseURLString:[[mainResource URL] _web_originalDataAsString]]; 281 [markupString release]; 282 return fragment; 283 } else if (MIMETypeRegistry::isSupportedImageMIMEType(MIMEType)) { 284 return [self _documentFragmentWithImageResource:mainResource]; 285 286 } 287 } 288 return nil; 289} 290 291- (DOMDocumentFragment *)_documentFragmentWithImageResource:(WebResource *)resource 292{ 293 DOMElement *imageElement = [self _imageElementWithImageResource:resource]; 294 if (!imageElement) 295 return 0; 296 DOMDocumentFragment *fragment = [[[self webFrame] DOMDocument] createDocumentFragment]; 297 [fragment appendChild:imageElement]; 298 return fragment; 299} 300 301- (DOMElement *)_imageElementWithImageResource:(WebResource *)resource 302{ 303 if (!resource) 304 return 0; 305 306 [self addSubresource:resource]; 307 308 DOMElement *imageElement = [[[self webFrame] DOMDocument] createElement:@"img"]; 309 310 // FIXME: calling _web_originalDataAsString on a file URL returns an absolute path. Workaround this. 311 NSURL *URL = [resource URL]; 312 [imageElement setAttribute:@"src" value:[URL isFileURL] ? [URL absoluteString] : [URL _web_originalDataAsString]]; 313 314 return imageElement; 315} 316 317// May return nil if not initialized with a URL. 318- (NSURL *)_URL 319{ 320 const KURL& url = _private->loader->url(); 321 if (url.isEmpty()) 322 return nil; 323 return url; 324} 325 326- (WebView *)_webView 327{ 328 return [[self webFrame] webView]; 329} 330 331- (BOOL)_isDocumentHTML 332{ 333 NSString *MIMEType = [self _responseMIMEType]; 334 return [WebView canShowMIMETypeAsHTML:MIMEType]; 335} 336 337-(void)_makeRepresentation 338{ 339 Class repClass = [[self class] _representationClassForMIMEType:[self _responseMIMEType]]; 340 341 // Check if the data source was already bound? 342 if (![[self representation] isKindOfClass:repClass]) { 343 id newRep = repClass != nil ? [[repClass alloc] init] : nil; 344 [self _setRepresentation:(id <WebDocumentRepresentation>)newRep]; 345 [newRep release]; 346 } 347 348 [_private->representation setDataSource:self]; 349} 350 351- (DocumentLoader*)_documentLoader 352{ 353 return _private->loader; 354} 355 356- (id)_initWithDocumentLoader:(PassRefPtr<WebDocumentLoaderMac>)loader 357{ 358 self = [super init]; 359 if (!self) 360 return nil; 361 362 _private = [[WebDataSourcePrivate alloc] init]; 363 364 _private->loader = loader.releaseRef(); 365 366 LOG(Loading, "creating datasource for %@", static_cast<NSURL *>(_private->loader->request().url())); 367 368 ++WebDataSourceCount; 369 370 return self; 371} 372 373@end 374 375@implementation WebDataSource 376 377- (id)initWithRequest:(NSURLRequest *)request 378{ 379 return [self _initWithDocumentLoader:WebDocumentLoaderMac::create(request, SubstituteData())]; 380} 381 382- (void)dealloc 383{ 384 --WebDataSourceCount; 385 386 [_private release]; 387 388 [super dealloc]; 389} 390 391- (void)finalize 392{ 393 --WebDataSourceCount; 394 395 [super finalize]; 396} 397 398- (NSData *)data 399{ 400 RefPtr<SharedBuffer> mainResourceData = _private->loader->mainResourceData(); 401 if (!mainResourceData) 402 return nil; 403 return [mainResourceData->createNSData() autorelease]; 404} 405 406- (id <WebDocumentRepresentation>)representation 407{ 408 return _private->representation; 409} 410 411- (WebFrame *)webFrame 412{ 413 FrameLoader* frameLoader = _private->loader->frameLoader(); 414 if (!frameLoader) 415 return nil; 416 return static_cast<WebFrameLoaderClient*>(frameLoader->client())->webFrame(); 417} 418 419- (NSURLRequest *)initialRequest 420{ 421 return _private->loader->originalRequest().nsURLRequest(); 422} 423 424- (NSMutableURLRequest *)request 425{ 426 FrameLoader* frameLoader = _private->loader->frameLoader(); 427 if (!frameLoader || !frameLoader->frameHasLoaded()) 428 return nil; 429 430 // FIXME: this cast is dubious 431 return (NSMutableURLRequest *)_private->loader->request().nsURLRequest(); 432} 433 434- (NSURLResponse *)response 435{ 436 return _private->loader->response().nsURLResponse(); 437} 438 439- (NSString *)textEncodingName 440{ 441 NSString *textEncodingName = _private->loader->overrideEncoding(); 442 if (!textEncodingName) 443 textEncodingName = [[self response] textEncodingName]; 444 return textEncodingName; 445} 446 447- (BOOL)isLoading 448{ 449 return _private->loader->isLoadingInAPISense(); 450} 451 452// Returns nil or the page title. 453- (NSString *)pageTitle 454{ 455 return [[self representation] title]; 456} 457 458- (NSURL *)unreachableURL 459{ 460 const KURL& unreachableURL = _private->loader->unreachableURL(); 461 if (unreachableURL.isEmpty()) 462 return nil; 463 return unreachableURL; 464} 465 466- (WebArchive *)webArchive 467{ 468 // it makes no sense to grab a WebArchive from an uncommitted document. 469 if (!_private->loader->isCommitted()) 470 return nil; 471 472 return [[[WebArchive alloc] _initWithCoreLegacyWebArchive:LegacyWebArchive::create(core([self webFrame]))] autorelease]; 473} 474 475- (WebResource *)mainResource 476{ 477 RefPtr<ArchiveResource> coreResource = _private->loader->mainResource(); 478 return [[[WebResource alloc] _initWithCoreResource:coreResource.release()] autorelease]; 479} 480 481- (NSArray *)subresources 482{ 483 Vector<PassRefPtr<ArchiveResource> > coreSubresources; 484 _private->loader->getSubresources(coreSubresources); 485 486 NSMutableArray *subresources = [[NSMutableArray alloc] initWithCapacity:coreSubresources.size()]; 487 for (unsigned i = 0; i < coreSubresources.size(); ++i) 488 [subresources addObject:[[[WebResource alloc] _initWithCoreResource:coreSubresources[i]] autorelease]]; 489 490 return [subresources autorelease]; 491} 492 493- (WebResource *)subresourceForURL:(NSURL *)URL 494{ 495 RefPtr<ArchiveResource> subresource = _private->loader->subresource(URL); 496 497 return subresource ? [[[WebResource alloc] _initWithCoreResource:subresource.get()] autorelease] : nil; 498} 499 500- (void)addSubresource:(WebResource *)subresource 501{ 502 _private->loader->addArchiveResource([subresource _coreResource]); 503} 504 505@end 506